var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
var __typeError = (msg) => {
  throw TypeError(msg);
};
var __using = (stack, value, async) => {
  if (value != null) {
    if (typeof value !== "object" && typeof value !== "function") __typeError("Object expected");
    var dispose, inner;
    if (async) dispose = value[__knownSymbol("asyncDispose")];
    if (dispose === void 0) {
      dispose = value[__knownSymbol("dispose")];
      if (async) inner = dispose;
    }
    if (typeof dispose !== "function") __typeError("Object not disposable");
    if (inner) dispose = function() {
      try {
        inner.call(this);
      } catch (e) {
        return Promise.reject(e);
      }
    };
    stack.push([async, dispose, value]);
  } else if (async) {
    stack.push([async]);
  }
  return value;
};
var __callDispose = (stack, error, hasError) => {
  var E = typeof SuppressedError === "function" ? SuppressedError : function(e, s, m, _) {
    return _ = Error(m), _.name = "SuppressedError", _.error = e, _.suppressed = s, _;
  };
  var fail = (e) => error = hasError ? new E(e, error, "An error was suppressed during disposal") : (hasError = true, e);
  var next = (it) => {
    while (it = stack.pop()) {
      try {
        var result = it[1] && it[1].call(it[2]);
        if (it[0]) return Promise.resolve(result).then(next, (e) => (fail(e), next()));
      } catch (e) {
        fail(e);
      }
    }
    if (hasError) throw error;
  };
  return next();
};
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { BasicSet, Environmental, Millis, Time, Timestamp } from "#general";
class ClientSubscriptions {
  #lifetime;
  #active = new BasicSet();
  #peers = /* @__PURE__ */ new Map();
  #timeout;
  constructor(lifetime) {
    this.#lifetime = lifetime.join("client subscriptions");
  }
  static [Environmental.create](env) {
    const instance = new ClientSubscriptions(env);
    env.set(ClientSubscriptions, instance);
    return instance;
  }
  join(...name) {
    return this.#lifetime.join(...name);
  }
  /**
   * Register a user-facing {@link ClientSubscription}.
   */
  addActive(subscription) {
    this.#active.add(subscription);
  }
  /**
   * Register a {@link PeerSubscription}.
   */
  addPeer(subscription) {
    let forPeer = this.#peers.get(subscription.peer);
    if (forPeer === void 0) {
      this.#peers.set(subscription.peer, forPeer = /* @__PURE__ */ new Map());
    }
    forPeer.set(subscription.subscriptionId, subscription);
    this.resetTimer();
  }
  /**
   * Retrieve a {@link PeerSubscription} by ID.
   */
  getPeer(address, id) {
    return this.#peers.get(address)?.get(id);
  }
  /**
   * Unregister a {@link PeerSubscription}.
   */
  delete(subscription) {
    const forPeer = this.#peers.get(subscription.peer);
    if (forPeer?.delete(subscription.subscriptionId)) {
      if (!forPeer.size) {
        this.#peers.delete(subscription.peer);
      }
    }
    this.#active.delete(subscription);
  }
  /**
   * Iterate over active subscriptions.
   */
  [Symbol.iterator]() {
    return this.#active[Symbol.iterator]();
  }
  /**
   * Terminate all subscriptions.
   */
  async close() {
    var _stack = [];
    try {
      const _closing = __using(_stack, this.#lifetime.closing());
      if (this.#timeout) {
        this.#timeout.stop();
        this.#timeout = void 0;
      }
      for (const subscription of this.#active) {
        subscription.close();
      }
      await this.#active.empty;
    } catch (_) {
      var _error = _, _hasError = true;
    } finally {
      __callDispose(_stack, _error, _hasError);
    }
  }
  /**
   * Restart the timeout timer for the current set of active subscriptions.
   */
  resetTimer() {
    const now = Time.nowMs;
    let nextTimeoutAt;
    for (const peer of this.#peers.values()) {
      for (const subscription of peer.values()) {
        if (subscription.isReading) {
          continue;
        }
        let { timeoutAt } = subscription;
        if (timeoutAt === void 0) {
          timeoutAt = subscription.timeoutAt = Timestamp(now + subscription.timeout);
        } else if (timeoutAt < now) {
          subscription.timedOut();
          continue;
        }
        if (nextTimeoutAt === void 0 || nextTimeoutAt > timeoutAt) {
          nextTimeoutAt = timeoutAt;
        }
      }
    }
    if (nextTimeoutAt === void 0) {
      this.#timeout?.stop();
      return;
    }
    if (this.#timeout) {
      this.#timeout?.stop();
      this.#timeout.interval = Millis(nextTimeoutAt - now);
    } else {
      this.#timeout = Time.getTimer(
        "Subscription timeout",
        Millis(nextTimeoutAt - now),
        this.resetTimer.bind(this)
      );
    }
    this.#timeout.start();
  }
}
export {
  ClientSubscriptions
};
//# sourceMappingURL=ClientSubscriptions.js.map
