"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 ControllerBehavior_exports = {};
__export(ControllerBehavior_exports, {
  ControllerBehavior: () => ControllerBehavior
});
module.exports = __toCommonJS(ControllerBehavior_exports);
var import_Behavior = require("#behavior/Behavior.js");
var import_basic_information = require("#behaviors/basic-information");
var import_Node = require("#node/Node.js");
var import_IdentityService = require("#node/server/IdentityService.js");
var import_general = require("@matter/general");
var import_protocol = require("@matter/protocol");
var import_types = require("@matter/types");
var import_CommissioningServer = require("../commissioning/CommissioningServer.js");
var import_NetworkServer = require("../network/NetworkServer.js");
var import_ActiveDiscoveries = require("./discovery/ActiveDiscoveries.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const logger = import_general.Logger.get("ControllerBehavior");
class ControllerBehavior extends import_Behavior.Behavior {
  static id = "controller";
  async initialize() {
    if (this.state.adminFabricLabel === void 0 || this.state.adminFabricLabel === "") {
      throw new import_general.ImplementationError("adminFabricLabel must be set for ControllerBehavior");
    }
    const node = import_Node.Node.forEndpoint(this.endpoint);
    if (this.state.ip === void 0) {
      this.state.ip = true;
    }
    if (this.state.ip !== false) {
      this.internal.services = this.env.asDependent();
      await this.internal.services.load(import_protocol.MdnsService);
    }
    if (this.state.ble === void 0) {
      this.state.ble = (await this.agent.load(import_NetworkServer.NetworkServer)).state.ble;
    }
    if (this.state.ble !== false) {
      try {
        this.env.get(import_protocol.ScannerSet).add(this.env.get(import_protocol.Ble).scanner);
      } catch (error) {
        logger.error("Disabling BLE due to initialization error:", error);
        this.state.ble = false;
      }
    }
    const authority = await this.env.load(import_protocol.FabricAuthority);
    const commissioning = this.agent.get(import_CommissioningServer.CommissioningServer);
    if (commissioning.state.enabled === void 0) {
      const totalFabrics = this.env.get(import_protocol.FabricManager).length;
      const controlledFabrics = authority.fabrics.length;
      if (controlledFabrics === totalFabrics) {
        commissioning.state.enabled = false;
      }
    }
    this.reactTo(node.lifecycle.online, this.#nodeOnline);
    if (node.lifecycle.isOnline) {
      await this.#nodeOnline();
    }
    this.reactTo(node.lifecycle.goingOffline, this.#nodeGoingOffline);
    const identity = this.env.get(import_IdentityService.IdentityService);
    const peers = this.env.get(import_protocol.PeerSet);
    this.reactTo(peers.added, (peer) => identity.reservePeerAddress(peer.address));
    this.reactTo(peers.deleted, (peer) => identity.releasePeerAddress(peer.address));
    for (const fabric of authority.fabrics) {
      identity.reservePeerAddress((0, import_protocol.PeerAddress)({ fabricIndex: fabric.fabricIndex, nodeId: fabric.nodeId }));
    }
    this.reactTo(
      authority.fabricAdded,
      (fabric) => identity.reservePeerAddress((0, import_protocol.PeerAddress)({ fabricIndex: fabric.fabricIndex, nodeId: fabric.nodeId }))
    );
  }
  /**
   * Allocate a new node address in the given fabric.
   */
  async allocatePeerAddress(fabricIndex, nodeId) {
    const identity = this.env.get(import_IdentityService.IdentityService);
    let address;
    try {
      return await this.endpoint.act(async function(agent) {
        const controller = agent.get(ControllerBehavior);
        await agent.context.transaction.addResources(controller);
        await agent.context.transaction.begin();
        if (nodeId !== void 0 && identity.peerAddressInUse({ fabricIndex, nodeId })) {
          throw new import_IdentityService.IdentityConflictError(
            `Cannot assign NodeId ${nodeId} on fabric ${fabricIndex}: the peer address is already in use`
          );
        }
        const useSequentialIds = controller.state.nodeIdAssignment !== "random";
        let nextNodeId = controller.state.nextNodeId ?? (0, import_types.NodeId)(1);
        while (nodeId === void 0) {
          if (useSequentialIds) {
            nodeId = nextNodeId;
            nextNodeId++;
          } else {
            nodeId = import_types.NodeId.randomOperationalNodeId(controller.env.get(import_general.Crypto));
          }
          if (identity.peerAddressInUse({ fabricIndex, nodeId })) {
            nodeId = void 0;
          }
        }
        address = (0, import_protocol.PeerAddress)({ fabricIndex, nodeId });
        identity.reservePeerAddress(address);
        if (useSequentialIds) {
          controller.state.nextNodeId = nextNodeId;
        }
        return address;
      });
    } catch (e) {
      if (address) {
        identity.releasePeerAddress(address);
      }
      throw e;
    }
  }
  async [Symbol.asyncDispose]() {
    await this.env.close(import_ActiveDiscoveries.ActiveDiscoveries);
    await this.internal.scanner?.close();
    this.env.delete(import_protocol.FabricAuthority);
    this.env.delete(import_protocol.ScannerSet);
    await this.internal.services?.close();
  }
  get fabricAuthorityConfig() {
    const biState = this.endpoint.stateOf(import_basic_information.BasicInformationBehavior);
    return {
      adminVendorId: biState.vendorId,
      ...this.state
    };
  }
  async #nodeOnline() {
    if (this.state.ip !== false && this.internal.scanner === void 0) {
      const mdns = this.env.get(import_protocol.MdnsService);
      this.internal.scanner = new import_protocol.CommissionableMdnsScanner(mdns.names);
      this.env.get(import_protocol.ScannerSet).add(this.internal.scanner);
    }
    const netTransports = this.env.get(import_general.TransportSet);
    if (this.state.ble) {
      netTransports.add(this.env.get(import_protocol.Ble).centralInterface);
    }
    const authority = this.env.get(import_protocol.FabricAuthority);
    for (const fabric of authority.fabrics) {
      if (fabric.label !== this.state.adminFabricLabel) {
        await fabric.setLabel(this.state.adminFabricLabel);
      }
      this.#enableScanningForFabric(fabric);
    }
    this.reactTo(authority.fabricAdded, this.#enableScanningForFabric);
  }
  async #nodeGoingOffline() {
    await this.env.close(import_protocol.ClientSubscriptions);
    const netTransports = this.env.get(import_general.TransportSet);
    if (this.state.ble) {
      netTransports.delete(this.env.get(import_protocol.Ble).centralInterface);
    }
  }
  #enableScanningForFabric(fabric) {
    if (this.internal.services) {
      const names = this.env.get(import_protocol.MdnsService).names;
      names.solicitor.solicit({
        name: names.get((0, import_protocol.getFabricQname)(fabric.globalId)),
        recordTypes: [import_general.DnsRecordType.PTR]
      });
    }
  }
}
((ControllerBehavior2) => {
  class Internal {
    services;
    scanner;
  }
  ControllerBehavior2.Internal = Internal;
  class State {
    /**
     * Set to false to disable scanning on BLE.
     *
     * By default the controller scans via BLE if BLE is available.
     */
    ble = void 0;
    /**
     * Set to false to disable scanning on IP networks.
     *
     * By default the controller always scans on IP networks.
     */
    ip = void 0;
    /**
     * Node ID assignment strategy.
     */
    nodeIdAssignment = "sequential";
    /**
     * Next assigned ID when {@link nodeIdAssignment} is "sequential".
     *
     * matter.js increments this value automatically after allocating a new node ID.  This means that the
     * "sequential" strategy does not reuse IDs from decommissioned nodes.
     *
     * If there is a conflict with an existing ID, matter.js increments this value until it identifies a free ID.
     */
    nextNodeId;
    /**
     * Contains the label of the admin fabric which is set for all commissioned devices
     */
    adminFabricLabel = "matter.js";
    /**
     * Contains the FabricId of the admin fabric when a defined number needs to be used because special Certificates
     * are used.
     * If not provided, a random FabricId will be generated.
     */
    adminFabricId = void 0;
    /**
     * Contains the NodeId of the admin node when a defined number needs to be used because special Certificates
     * are used.
     * If not provided, a random NodeId will be generated.
     */
    adminNodeId = void 0;
    /**
     * Case Authenticated Tags to be used to commission and connect to devices.
     */
    caseAuthenticatedTags = void 0;
  }
  ControllerBehavior2.State = State;
})(ControllerBehavior || (ControllerBehavior = {}));
//# sourceMappingURL=ControllerBehavior.js.map
