"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, {
  SubscriptionsBehavior: () => SubscriptionsBehavior
});
module.exports = __toCommonJS(SubscriptionsServer_exports);
var import_general = require("#general");
var import_model = require("#model");
var import_InteractionServer = require("#node/server/InteractionServer.js");
var import_ServerSubscription = require("#node/server/ServerSubscription.js");
var import_protocol = require("#protocol");
var import_types = require("#types");
var import_Behavior = require("../../Behavior.js");
var import_SessionsBehavior = require("../sessions/SessionsBehavior.js");
/**
 * @license
 * Copyright 2022-2025 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 SubscriptionsBehavior 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" }),
        (0, import_model.FieldElement)(
          {
            name: "operationalAddress",
            type: "struct",
            conformance: "O"
          },
          (0, import_model.FieldElement)({ name: "type", type: "string" }),
          (0, import_model.FieldElement)({ name: "ip", type: "string" }),
          (0, import_model.FieldElement)({ name: "port", type: "uint16" })
        )
      )
    )
  );
  #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;
    let operationalAddress;
    try {
      operationalAddress = (0, import_general.isIpNetworkChannel)(session.channel) ? session.channel.networkAddress : void 0;
    } catch (error) {
      import_protocol.SessionClosedError.accept(error);
    }
    const peerSubscription = {
      subscriptionId: id,
      peerAddress: { fabricIndex, nodeId },
      maxIntervalCeiling,
      minIntervalFloor,
      attributeRequests,
      eventRequests,
      isFabricFiltered,
      maxInterval,
      sendInterval,
      operationalAddress
    };
    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 sessions = this.env.get(import_protocol.SessionManager);
    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 successfullReEstablishments = Array();
    for (const subscription of formerSubscriptions) {
      const { peerAddress: peerAddressDetails, operationalAddress, subscriptionId } = subscription;
      const peerAddress = (0, import_protocol.PeerAddress)(peerAddressDetails);
      if (peerStopList.has(peerAddress)) {
        logger.debug(`Skip reestablishing former subscription to ${peerAddress}`);
        continue;
      }
      logger.debug(
        `Try to reestablish former subscription ${import_protocol.Subscription.idStrOf(subscription)} to ${peerAddress}`
      );
      if (sessions.maybeSessionFor(peerAddress) !== void 0) {
        logger.debug(`We already have and existing session for peer ${peerAddress}`);
      } else {
        try {
          await peers.connect(peerAddress, {
            discoveryOptions: {
              discoveryType: import_protocol.NodeDiscoveryType.TimedDiscovery,
              timeout: REESTABLISH_SUBSCRIPTIONS_TIMEOUT
            },
            operationalAddress
          });
        } catch (error) {
          peerStopList.add(peerAddress);
          logger.debug(
            `Failed to connect to ${peerAddress}`,
            error instanceof import_general.MatterError ? error.message : error
          );
          continue;
        }
      }
      try {
        if (peerStopList.has(peerAddress)) {
          logger.debug(
            `Skip re-establishing former subscription ${import_protocol.Subscription.idStrOf(subscriptionId)} to ${peerAddress}`
          );
          continue;
        }
        const session = sessions.maybeSessionFor(peerAddress);
        if (session === void 0) {
          peerStopList.add(peerAddress);
          logger.debug(`Could not connect to peer ${peerAddress}`);
          continue;
        }
        await interactionServer.establishFormerSubscription(subscription, session);
      } catch (error) {
        logger.debug(
          `Failed to re-establish former subscription ${import_protocol.Subscription.idStrOf(subscriptionId)} to ${peerAddress}`,
          import_types.StatusResponseError.is(error) ? error.code === import_types.StatusCode.InvalidSubscription ? "Subscription no langer valid for peer" : error.message : error
        );
        continue;
      }
      successfullReEstablishments.push(subscriptionId);
    }
    interactionServer.subscriptionEstablishmentStarted.off(blockHandler);
    logger.info(
      `Reestablished ${successfullReEstablishments.length}${successfullReEstablishments.length ? ` (${successfullReEstablishments.join(",")})` : ""} of ${formerSubscriptions.length} former subscriptions successfully`
    );
  }
}
((SubscriptionsBehavior2) => {
  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 = [];
  }
  SubscriptionsBehavior2.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();
  }
  SubscriptionsBehavior2.Internal = Internal;
})(SubscriptionsBehavior || (SubscriptionsBehavior = {}));
//# sourceMappingURL=SubscriptionsServer.js.map
