"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 CommissioningServer_exports = {};
__export(CommissioningServer_exports, {
  CommissioningServer: () => CommissioningServer
});
module.exports = __toCommonJS(CommissioningServer_exports);
var import_SubscriptionsServer = require("#behavior/system/subscriptions/SubscriptionsServer.js");
var import_EndpointType = require("#endpoint/type/EndpointType.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_BasicInformationBehavior = require("../../../behaviors/basic-information/BasicInformationBehavior.js");
var import_OperationalCredentialsBehavior = require("../../../behaviors/operational-credentials/OperationalCredentialsBehavior.js");
var import_Behavior = require("../../Behavior.js");
var import_NetworkServer = require("../network/NetworkServer.js");
var import_ProductDescriptionServer = require("../product-description/ProductDescriptionServer.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("Commissioning");
class CommissioningServer extends import_Behavior.Behavior {
  static id = "commissioning";
  static early = true;
  initialize() {
    this.internal.name = this.toString();
    if (this.state.passcode === -1) {
      this.state.passcode = import_protocol.PaseClient.generateRandomPasscode(this.env.get(import_general.Crypto));
    } else if (import_types.CommissioningOptions.FORBIDDEN_PASSCODES.includes(this.state.passcode)) {
      throw new import_general.ImplementationError(`Passcode ${this.state.passcode} is not allowed`);
    }
    if (this.state.discriminator === -1) {
      this.state.discriminator = import_protocol.PaseClient.generateRandomDiscriminator(this.env.get(import_general.Crypto));
    }
    this.reactTo(this.endpoint.lifecycle.partsReady, this.#initializeNode);
    if (!this.env.has(import_protocol.CommissioningConfigProvider)) {
      this.env.set(
        import_protocol.CommissioningConfigProvider,
        new CommissioningServerConfigProvider(this.endpoint)
      );
    }
    this.reactTo(this.endpoint.lifecycle.online, this.#enterOnlineMode);
    this.reactTo(this.endpoint.lifecycle.goingOffline, this.#enterOfflineMode);
  }
  async [Symbol.asyncDispose]() {
    this.#enterOfflineMode();
    await this.internal.mutex;
  }
  handleFabricChange(fabricIndex, fabricAction) {
    if (this.env.has(import_protocol.FailsafeContext)) {
      const failsafe = this.env.get(import_protocol.FailsafeContext);
      if (fabricAction === "added" || fabricAction === "updated") {
        if (failsafe.construction.status !== import_general.Lifecycle.Status.Destroyed) {
          if (failsafe.fabricIndex === fabricIndex) {
            this.#monitorFailsafe(failsafe);
            return;
          } else {
            throw new import_general.MatterFlowError(
              `Failsafe owns a different fabricIndex then ${failsafe.forUpdateNoc ? "updated" : "added"}: ${failsafe.fabricIndex} vs. ${fabricIndex}`
            );
          }
        }
      } else if (fabricAction === "deleted") {
        if (failsafe.fabricIndex !== fabricIndex) {
          throw new import_general.MatterFlowError(
            `Failsafe owns a different fabricIndex then removed: ${failsafe.fabricIndex} vs. ${fabricIndex}`
          );
        }
      }
    }
    const commissioned = !!this.env.get(import_protocol.FabricManager).fabrics.length;
    let doFactoryReset = false;
    if (commissioned !== this.state.commissioned) {
      this.state.commissioned = commissioned;
      if (commissioned) {
        this.events.commissioned.emit(this.context);
        this.endpoint.lifecycle.commissioned.emit(this.context);
      } else {
        this.events.decommissioned.emit(this.context);
        this.endpoint.lifecycle.decommissioned.emit(this.context);
        doFactoryReset = true;
      }
    }
    this.events.fabricsChanged.emit(fabricIndex, fabricAction);
    if (doFactoryReset) {
      const sessions = this.agent.get(import_SessionsBehavior.SessionsBehavior);
      if (Object.keys(sessions.state.sessions).length > 0) {
        this.reactTo(sessions.events.closed, this.#resetAfterSessionsClear);
      } else {
        this.#triggerFactoryReset();
      }
    }
  }
  #resetAfterSessionsClear() {
    const sessions = this.agent.get(import_SessionsBehavior.SessionsBehavior);
    if (Object.keys(sessions.state.sessions).length === 0) {
      this.#triggerFactoryReset();
    }
  }
  #triggerFactoryReset() {
    this.env.runtime.add(this.endpoint.erase().catch((e) => import_general.MutexClosedError.accept(e)));
  }
  #monitorFailsafe(failsafe) {
    if (this.internal.unregisterFailsafeListener) {
      return;
    }
    const listener = this.callback(function(status) {
      if (status === import_general.Lifecycle.Status.Destroyed) {
        if (failsafe.fabricIndex !== void 0) {
          this.handleFabricChange(failsafe.fabricIndex, failsafe.forUpdateNoc ? "updated" : "added");
        }
        this.internal.unregisterFailsafeListener?.();
      }
    });
    this.internal.unregisterFailsafeListener = this.callback(function() {
      failsafe.construction.change.off(listener);
      this.internal.unregisterFailsafeListener = void 0;
    });
    failsafe.construction.change.on(listener);
  }
  async #enterOnlineMode() {
    this.reactTo(this.env.get(import_protocol.FabricManager).events.added, this.enterOperationalMode);
    if (this.endpoint.lifecycle.isCommissioned) {
      await this.endpoint.act((agent) => agent.get(import_SubscriptionsServer.SubscriptionsServer).reestablishFormerSubscriptions());
      this.enterOperationalMode();
      return;
    }
    if (this.state.enabled === false) {
      return;
    }
    if (this.env.get(import_protocol.FabricManager).fabrics.length) {
      return;
    }
    if (!this.#hasAdvertisableDeviceType) {
      return;
    }
    await this.enterCommissionableMode();
  }
  #enterOfflineMode() {
    this.internal.mutex.run(async () => {
      await this.env.close(import_protocol.DeviceCommissioner);
      this.env.delete(import_protocol.CommissioningConfigProvider);
      this.internal.unregisterFailsafeListener?.();
      await this.env.close(import_protocol.FailsafeContext);
    });
  }
  /**
   * An uncommissioned node is not yet associated with fabrics.  It cannot be used until commissioned by a controller.
   *
   * The server normally invokes this method when the node starts and is not yet commissioned.  You can disable by
   * setting {@link CommissioningServer.State#enabled} to false.  Then you must invoke yourself.
   */
  async enterCommissionableMode() {
    if (!this.#hasAdvertisableDeviceType) {
      throw new import_general.ImplementationError(
        `Node ${this.endpoint} has no endpoints with advertisable device types; you must add an endpoint or set the device type`
      );
    }
    await this.env.get(import_protocol.DeviceCommissioner).allowBasicCommissioning();
    this.initiateCommissioning();
  }
  /**
   * The server invokes this method when the node starts and is already commissioned, or immediately after
   * commissioning.
   */
  enterOperationalMode() {
    if (!this.endpoint.lifecycle.isOnline) {
      throw new import_general.ImplementationError("Cannot advertise offline server");
    }
    this.env.get(import_protocol.DeviceAdvertiser).enterOperationalMode();
  }
  /**
   * Display instructions on commissioning the device.
   *
   * The default implementation logs the QR code and credentials.
   */
  initiateCommissioning() {
    const { passcode, discriminator } = this.state;
    const { qrPairingCode, manualPairingCode } = this.state.pairingCodes;
    logger.notice(
      import_general.Diagnostic.strong(this.endpoint.toString()),
      "is uncommissioned",
      import_general.Diagnostic.dict({
        passcode,
        discriminator,
        "manual pairing code": manualPairingCode
      }),
      import_general.Diagnostic.list([
        import_types.QrCode.get(qrPairingCode).trim(),
        `QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=${qrPairingCode}
`
      ])
    );
  }
  /**
   * Define logical schema to make passcode and discriminator persistent.
   */
  static schema = new import_model.DatatypeModel({
    name: "CommissioningState",
    type: "struct",
    children: [
      (0, import_model.FieldElement)({ name: "passcode", type: "uint32", quality: "N" }),
      (0, import_model.FieldElement)({ name: "discriminator", type: "uint16", quality: "N" })
    ]
  });
  #initializeNode() {
    this.state.commissioned = !!this.agent.get(import_OperationalCredentialsBehavior.OperationalCredentialsBehavior).state.commissionedFabrics;
    this.endpoint.lifecycle.initialized.emit(this.state.commissioned);
  }
  get #hasAdvertisableDeviceType() {
    return this.agent.get(import_ProductDescriptionServer.ProductDescriptionServer).state.deviceType !== import_EndpointType.EndpointType.UNKNOWN_DEVICE_TYPE;
  }
}
((CommissioningServer2) => {
  class Internal {
    unregisterFailsafeListener = void 0;
    /**
     * We use this to synchronize internal state transitions that would otherwise have race conditions due to the
     * large number of asynchronous calls.
     */
    mutex = new import_general.Mutex({
      toString: () => this.name
    });
    /**
     * Passed to the mutex for error handling purposes
     */
    name = "?";
  }
  CommissioningServer2.Internal = Internal;
  class State {
    enabled;
    commissioned = false;
    fabrics = {};
    passcode = -1;
    discriminator = -1;
    flowType = import_types.CommissioningFlowType.Standard;
    additionalBleAdvertisementData = void 0;
    pairingCodes = {};
    mdns;
    ble;
    [import_protocol.Val.properties](endpoint) {
      const comm = this;
      return {
        get pairingCodes() {
          const bi = endpoint.stateOf(import_BasicInformationBehavior.BasicInformationBehavior);
          const net = endpoint.stateOf(import_NetworkServer.NetworkServer);
          const qrPairingCode = import_types.QrPairingCodeCodec.encode([
            {
              version: 0,
              vendorId: bi.vendorId,
              productId: bi.productId,
              flowType: comm.flowType,
              discriminator: comm.discriminator,
              passcode: comm.passcode,
              discoveryCapabilities: import_types.DiscoveryCapabilitiesSchema.encode(net.discoveryCapabilities)
            }
          ]);
          return {
            manualPairingCode: import_types.ManualPairingCodeCodec.encode({
              // We use -1 to flag "need generated value" but this will crash the pairing code generator.  So use 0
              // so we don't throw an error during initialization
              discriminator: comm.discriminator < 0 ? 0 : comm.discriminator,
              passcode: comm.passcode < 0 ? 0 : comm.passcode
            }),
            qrPairingCode
          };
        },
        get fabrics() {
          const exposedFabrics = {};
          endpoint.env.get(import_protocol.FabricManager).fabrics.forEach(
            ({ fabricIndex, externalInformation }) => exposedFabrics[fabricIndex] = externalInformation
          );
          return exposedFabrics;
        }
      };
    }
  }
  CommissioningServer2.State = State;
  class Events extends import_general.EventEmitter {
    commissioned = (0, import_general.Observable)();
    decommissioned = (0, import_general.Observable)();
    fabricsChanged = (0, import_general.Observable)();
    enabled$Changed = (0, import_general.AsyncObservable)();
  }
  CommissioningServer2.Events = Events;
})(CommissioningServer || (CommissioningServer = {}));
class CommissioningServerConfigProvider extends import_protocol.CommissioningConfigProvider {
  #node;
  constructor(node) {
    super();
    this.#node = node;
  }
  get values() {
    const { commissioning, productDescription, network } = this.#node.state;
    const config = {
      ...commissioning,
      productDescription,
      ble: !!network.ble
    };
    return config;
  }
}
//# sourceMappingURL=CommissioningServer.js.map
