"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var SustainedSubscription_exports = {};
__export(SustainedSubscription_exports, {
  SustainedSubscription: () => SustainedSubscription
});
module.exports = __toCommonJS(SustainedSubscription_exports);
var import_general = require("@matter/general");
var import_model = require("@matter/model");
var import_ClientSubscription = require("./ClientSubscription.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const logger = import_general.Logger.get("ClientSubscription");
class SustainedSubscription extends import_ClientSubscription.ClientSubscription {
  #request;
  #subscription;
  #retries;
  #subscribe;
  #read;
  #probe;
  #active = (0, import_general.AsyncObservableValue)(false);
  #inactive = (0, import_general.AsyncObservableValue)(true);
  constructor(config) {
    super(config);
    const { request, read, probe, retries, subscribe } = config;
    this.#request = request;
    this.#retries = retries;
    this.#subscribe = subscribe;
    this.#read = read;
    this.#probe = probe;
    this.done = this.#run();
  }
  /**
   * Emits when the active state changes.
   */
  get active() {
    return this.#active;
  }
  /**
   * Emits when inactive state changes.
   */
  get inactive() {
    return this.#inactive;
  }
  async #run() {
    let sessionTrusted = true;
    const updated = this.#request.updated?.bind(this.#request);
    let { bootstrapWithRead, refreshRequest } = this.#request;
    let needToRefreshRequest = false;
    while (true) {
      let request = { ...this.#request, updated };
      if (this.#request.updated) {
        request.updated = this.#request.updated.bind(request);
      }
      const closed = new Promise((resolve) => {
        request.closed = () => {
          this.#subscription = void 0;
          this.subscriptionId = import_ClientSubscription.ClientSubscription.NO_SUBSCRIPTION;
          sessionTrusted = false;
          resolve();
        };
      });
      if (!sessionTrusted) {
        if (!await this.#probe(this.abort)) {
          if (!this.abort.aborted) {
            sessionTrusted = true;
            logger.error(`Failed to probe reachability of peer ${this.peer}, resubscribe with new session`);
          }
        }
        if (this.abort.aborted) {
          return;
        }
      }
      for (const retry of this.#retries) {
        try {
          if (needToRefreshRequest && refreshRequest !== void 0) {
            request = refreshRequest(request);
          }
          needToRefreshRequest = true;
          if (bootstrapWithRead) {
            const response = this.#read(request, this.abort, import_general.Diagnostic.asFlags({ bootstrap: true }));
            if (request.updated) {
              await request.updated(response);
            } else {
              for await (const _chunk of response) ;
            }
            if (this.abort.aborted) {
              return;
            }
            bootstrapWithRead = false;
            if (refreshRequest !== void 0) {
              request = refreshRequest(request);
            }
          }
          this.#subscription = await this.#subscribe(request, this.abort);
          this.subscriptionId = this.#subscription.subscriptionId;
          sessionTrusted = true;
          break;
        } catch (e) {
          if (!(0, import_general.causedBy)(e, import_general.AbortedError) || !this.abort.aborted) {
            sessionTrusted = true;
            logger.error(
              `Failed to establish subscription to ${this.peer}, retry in ${import_general.Duration.format(retry)}:`,
              import_general.Diagnostic.errorMessage((0, import_general.asError)(e))
            );
          }
          if (this.abort.aborted) {
            return;
          }
        }
        const readyForRetry = import_general.Time.sleep("subscription retry", retry);
        await this.abort.race(readyForRetry);
        readyForRetry.cancel();
        if (this.abort.aborted) {
          break;
        }
      }
      await this.#inactive.emit(false);
      await this.#active.emit(true);
      if (this.abort.aborted) {
        break;
      }
      await closed;
      await this.#active.emit(false);
      await this.#inactive.emit(true);
      if (this.abort.aborted) {
        break;
      }
      logger.error(`Replacing subscription to ${this.peer} due to timeout`);
    }
    this.#request.closed?.();
  }
  get interactionModelRevision() {
    return this.#subscription?.interactionModelRevision ?? import_model.Specification.INTERACTION_MODEL_REVISION;
  }
  get maxInterval() {
    return this.#subscription?.maxInterval ?? import_general.Hours.one;
  }
}
((SustainedSubscription2) => {
  function assert(subscription) {
    if (!(subscription instanceof SustainedSubscription2)) {
      throw new import_general.ImplementationError(`Non-sustained subscription provided where sustained subscription required`);
    }
  }
  SustainedSubscription2.assert = assert;
  SustainedSubscription2.DefaultRetrySchedule = {
    // Protocol-level level happens at the exchange level and is faster; this is an application-level retry.  Retry
    // more slowly so we do not hammer devices that are experiencing transient errors
    initialInterval: (0, import_general.Seconds)(15),
    // Similarly, we have an exchange.  If a device repeatedly fails to establish a subscription, give it plenty of
    // time to recover.  It's even possible our subscription attempt is invalid for some reason, in which case we
    // an aggressive interval would be particularly bad form
    maximumInterval: (0, import_general.Hours)(1),
    // No timeout; we run until aborted
    timeout: void 0,
    backoffFactor: 2,
    jitterFactor: 0.25
  };
})(SustainedSubscription || (SustainedSubscription = {}));
//# sourceMappingURL=SustainedSubscription.js.map
