"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 SubscriptionsServer_exports = {};
__export(SubscriptionsServer_exports, {
  SubscriptionsServer: () => SubscriptionsServer
});
module.exports = __toCommonJS(SubscriptionsServer_exports);
var import_InteractionServer = require("#node/server/InteractionServer.js");
var import_ServerSubscription = require("#node/server/ServerSubscription.js");
var import_general = require("@matter/general");
var import_model = require("@matter/model");
var import_protocol = require("@matter/protocol");
var import_types = require("@matter/types");
var import_Behavior = require("../../Behavior.js");
var import_SessionsBehavior = require("../sessions/SessionsBehavior.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const logger = import_general.Logger.get("SubscriptionsBehavior");
const REESTABLISH_SUBSCRIPTIONS_TIMEOUT = (0, import_general.Seconds)(2);
class SubscriptionsServer extends import_Behavior.Behavior {
  static id = "subscriptions";
  initialize() {
    if (this.state.subscriptions !== void 0 && this.state.persistenceEnabled !== false) {
      this.internal.formerSubscriptions = (0, import_general.deepCopy)(this.state.subscriptions);
    }
    this.state.subscriptions = [];
    const sessions = this.agent.get(import_SessionsBehavior.SessionsBehavior);
    this.reactTo(sessions.events.subscriptionAdded, this.#addSubscription, { lock: true });
  }
  static schema = new import_model.DatatypeModel(
    {
      name: "SubscriptionState",
      type: "struct"
    },
    (0, import_model.FieldElement)(
      {
        name: "subscriptions",
        type: "list",
        quality: "N",
        conformance: "M",
        default: []
      },
      (0, import_model.FieldElement)(
        {
          name: "entry",
          type: "struct"
        },
        (0, import_model.FieldElement)({ name: "subscriptionId", type: "uint32" }),
        (0, import_model.FieldElement)(
          {
            name: "peerAddress",
            type: "struct"
          },
          (0, import_model.FieldElement)({ name: "fabricIndex", type: "fabric-id" }),
          (0, import_model.FieldElement)({ name: "nodeId", type: "node-id" })
        ),
        (0, import_model.FieldElement)(
          {
            name: "attributeRequests",
            type: "list"
          },
          (0, import_model.FieldElement)(
            {
              name: "entry",
              type: "struct"
            },
            (0, import_model.FieldElement)({ name: "enableTagCompression", type: "bool", conformance: "O" }),
            (0, import_model.FieldElement)({ name: "nodeId", type: "node-id", conformance: "O" }),
            (0, import_model.FieldElement)({ name: "endpointId", type: "endpoint-no", conformance: "O" }),
            (0, import_model.FieldElement)({ name: "clusterId", type: "cluster-id", conformance: "O" }),
            (0, import_model.FieldElement)({ name: "attributeId", type: "attrib-id", conformance: "O" }),
            (0, import_model.FieldElement)({ name: "listIndex", type: "uint16", conformance: "O" }),
            (0, import_model.FieldElement)({
              name: "wildcardPathFlags",
              type: "WildcardPathFlagsBitmap",
              conformance: "O"
            })
          )
        ),
        (0, import_model.FieldElement)(
          {
            name: "eventRequests",
            type: "list"
          },
          (0, import_model.FieldElement)(
            {
              name: "entry",
              type: "struct"
            },
            (0, import_model.FieldElement)({ name: "nodeId", type: "node-id", quality: "O" }),
            (0, import_model.FieldElement)({ name: "endpointId", type: "endpoint-no", quality: "O" }),
            (0, import_model.FieldElement)({ name: "clusterId", type: "cluster-id", quality: "O" }),
            (0, import_model.FieldElement)({ name: "eventId", type: "event-id", quality: "O" }),
            (0, import_model.FieldElement)({ name: "isUrgent", type: "bool", quality: "O" })
          )
        ),
        (0, import_model.FieldElement)({ name: "isFabricFiltered", type: "bool" }),
        (0, import_model.FieldElement)({ name: "maxIntervalCeiling", type: "duration" }),
        (0, import_model.FieldElement)({ name: "minIntervalFloor", type: "duration" }),
        (0, import_model.FieldElement)({ name: "maxInterval", type: "duration" }),
        (0, import_model.FieldElement)({ name: "sendInterval", type: "duration" })
      )
    )
  );
  #addSubscription(subscription) {
    if (this.state.persistenceEnabled === false || !(subscription instanceof import_ServerSubscription.ServerSubscription)) return;
    const {
      request: { attributeRequests, eventRequests, isFabricFiltered },
      session,
      maxInterval,
      sendInterval,
      subscriptionId: id,
      maxIntervalCeiling,
      minIntervalFloor
    } = subscription;
    const { peerAddress } = session;
    const { fabricIndex, nodeId } = peerAddress;
    const peerSubscription = {
      subscriptionId: id,
      peerAddress: { fabricIndex, nodeId },
      maxIntervalCeiling,
      minIntervalFloor,
      attributeRequests,
      eventRequests,
      isFabricFiltered,
      maxInterval,
      sendInterval
    };
    this.reactTo(subscription.cancelled, this.#subscriptionCancelled);
    const existingIndex = this.state.subscriptions.findIndex(({ subscriptionId }) => id === subscriptionId);
    if (existingIndex !== -1) {
      this.state.subscriptions[existingIndex] = peerSubscription;
      return;
    }
    this.state.subscriptions.push(peerSubscription);
  }
  #subscriptionCancelled(subscription) {
    if (subscription.isCanceledByPeer && this.state.persistenceEnabled !== false) {
      const { subscriptionId: id } = subscription;
      const subscriptionIndex = this.state.subscriptions.findIndex(({ subscriptionId }) => id === subscriptionId);
      if (subscriptionIndex !== -1) {
        return this.#removeSubscriptionIndex(subscriptionIndex);
      }
    }
  }
  async #removeSubscriptionIndex(index) {
    await this.context.transaction.addResources(this);
    await this.context.transaction.begin();
    this.state.subscriptions.splice(index, 1);
    await this.context.transaction.commit();
  }
  async reestablishFormerSubscriptions() {
    if (this.state.persistenceEnabled === false) return;
    const { formerSubscriptions } = this.internal;
    if (!formerSubscriptions.length) {
      logger.debug("No former subscriptions to re-establish");
      return;
    } else {
      this.internal.formerSubscriptions = [];
      await this.context.transaction.commit();
    }
    const peers = this.env.get(import_protocol.PeerSet);
    const interactionServer = this.env.get(import_InteractionServer.InteractionServer);
    const peerStopList = new import_protocol.PeerAddressSet();
    const blockHandler = (peerAddress) => void peerStopList.add(peerAddress);
    interactionServer.subscriptionEstablishmentStarted.on(blockHandler);
    const subscriptionsByPeer = new import_protocol.PeerAddressMap();
    for (const subscription of formerSubscriptions) {
      const peerAddress = (0, import_protocol.PeerAddress)(subscription.peerAddress);
      const existing = subscriptionsByPeer.get(peerAddress);
      if (existing !== void 0) {
        existing.push(subscription);
      } else {
        subscriptionsByPeer.set(peerAddress, [subscription]);
      }
    }
    const successfulReEstablishments = Array();
    try {
      await import_general.MatterAggregateError.allSettled(
        [...subscriptionsByPeer.entries()].map(async ([peerAddress, peerSubscriptions]) => {
          if (peerStopList.has(peerAddress)) {
            logger.debug(`Skip reestablishing former subscriptions to ${peerAddress}`);
            return;
          }
          let session;
          try {
            const peer = peers.addKnownPeer({ address: peerAddress });
            const { operationalAddress } = peer.descriptor;
            const preferredTransport = operationalAddress && "type" in operationalAddress && operationalAddress.type === "tcp" ? import_general.ChannelType.TCP : void 0;
            session = await peer.connect({
              connectionTimeout: REESTABLISH_SUBSCRIPTIONS_TIMEOUT,
              preferredTransport
            });
            if (import_protocol.GroupSession.is(session)) {
              return;
            }
          } catch (error) {
            peerStopList.add(peerAddress);
            logger.debug(
              `Failed to connect to ${peerAddress}`,
              error instanceof import_general.MatterError ? error.message : error
            );
            return;
          }
          for (const subscription of peerSubscriptions) {
            const { subscriptionId } = subscription;
            if (peerStopList.has(peerAddress)) {
              logger.debug(
                `Skip re-establishing former subscription ${import_protocol.Subscription.idStrOf(subscriptionId)} to ${peerAddress}`
              );
              continue;
            }
            logger.debug(
              `Try to reestablish former subscription ${import_protocol.Subscription.idStrOf(subscription)} to ${peerAddress}`
            );
            try {
              await interactionServer.establishFormerSubscription(subscription, session);
            } catch (error) {
              const sre = import_types.StatusResponseError.of(error);
              const isInvalidSubscription = sre?.code === import_types.Status.InvalidSubscription;
              logger.debug(
                `Failed to re-establish former subscription ${import_protocol.Subscription.idStrOf(subscriptionId)} to ${peerAddress}`,
                sre ? isInvalidSubscription ? "Subscription no longer valid for peer" : sre.message : error
              );
              if (isInvalidSubscription) {
                break;
              }
              continue;
            }
            successfulReEstablishments.push(subscriptionId);
          }
        })
      );
    } finally {
      interactionServer.subscriptionEstablishmentStarted.off(blockHandler);
    }
    logger.info(
      `Reestablished ${successfulReEstablishments.length}${successfulReEstablishments.length ? ` (${successfulReEstablishments.join(",")})` : ""} of ${formerSubscriptions.length} former subscriptions successfully`
    );
  }
}
((SubscriptionsServer2) => {
  class State {
    /** Set to false if persistence of subscriptions should be disabled */
    persistenceEnabled = true;
    /**
     * List of subscriptions. This list is collected automatically.
     * The state value should not be initialized by the developer.
     */
    subscriptions = [];
  }
  SubscriptionsServer2.State = State;
  class Internal {
    /**
     * Subscriptions that were established on the former device run. On initialization this will be initialized
     * with the persisted subscriptions and then used to re-establish the subscriptions.
     */
    formerSubscriptions = Array();
  }
  SubscriptionsServer2.Internal = Internal;
})(SubscriptionsServer || (SubscriptionsServer = {}));
//# sourceMappingURL=SubscriptionsServer.js.map
