"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 CommissioningBehavior_exports = {};
__export(CommissioningBehavior_exports, {
  CommissioningBehavior: () => CommissioningBehavior
});
module.exports = __toCommonJS(CommissioningBehavior_exports);
var import_general = require("#general");
var import_model = require("#model");
var import_protocol = require("#protocol");
var import_types = require("#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_Val = require("../../state/Val.js");
var import_NetworkServer = require("../network/NetworkServer.js");
var import_SessionsBehavior = require("../sessions/SessionsBehavior.js");
/**
 * @license
 * Copyright 2022-2024 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const logger = import_general.Logger.get("Commissioning");
class CommissioningBehavior extends import_Behavior.Behavior {
  static id = "commissioning";
  static early = true;
  initialize() {
    if (this.state.passcode === -1) {
      this.state.passcode = import_protocol.PaseClient.generateRandomPasscode();
    } 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.reactTo(this.endpoint.lifecycle.online, this.#nodeOnline);
    this.reactTo(this.endpoint.lifecycle.partsReady, this.#initializeNode);
  }
  [Symbol.asyncDispose]() {
    this.internal.unregisterFailsafeListener?.();
  }
  handleFabricChange(fabricIndex, fabricAction) {
    if (this.env.has(import_protocol.FailsafeContext)) {
      const failsafe = this.env.get(import_protocol.FailsafeContext);
      if (fabricAction === import_protocol.FabricAction.Added || fabricAction === import_protocol.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"}.`
            );
          }
        }
      } else if (fabricAction === import_protocol.FabricAction.Removed) {
        if (failsafe.fabricIndex !== fabricIndex) {
          throw new import_general.MatterFlowError("Failsafe owns a different fabricIndex then removed.");
        }
      }
    }
    const fabrics = this.env.get(import_protocol.FabricManager).getFabrics();
    const commissioned = !!fabrics.length;
    if (fabricAction === import_protocol.FabricAction.Removed) {
      delete this.state.fabrics[fabricIndex];
    } else {
      const fabric = fabrics.find((fabric2) => fabric2.fabricIndex === fabricIndex);
      if (fabric !== void 0) {
        this.state.fabrics[fabricIndex] = {
          fabricIndex: fabric.fabricIndex,
          fabricId: fabric.fabricId,
          nodeId: fabric.nodeId,
          rootNodeId: fabric.rootNodeId,
          rootVendorId: fabric.rootVendorId,
          label: fabric.label
        };
      }
    }
    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.#handleSessionClosed);
      } else {
        this.#triggerFactoryReset();
      }
    }
  }
  #handleSessionClosed() {
    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.factoryReset().then(this.callback(this.initiateCommissioning))
    );
  }
  #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 ? import_protocol.FabricAction.Updated : import_protocol.FabricAction.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);
  }
  /**
   * The server invokes this method when the node is active but not yet commissioned.
   *
   * An uncommissioned node is not yet associated with fabrics.  It cannot be used until commissioned by a controller.
   *
   * 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}
`
      ])
    );
  }
  /**
   * Obtain pairing codes for a node.
   */
  static pairingCodesFor(node) {
    const bi = node.stateOf(import_BasicInformationBehavior.BasicInformationBehavior);
    const comm = node.stateOf(CommissioningBehavior);
    const net = node.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({
        discriminator: comm.discriminator,
        passcode: comm.passcode
      }),
      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" })
    ]
  });
  #nodeOnline() {
    const fabrics = this.env.get(import_protocol.FabricManager).getFabrics();
    if (!fabrics.length) {
      this.initiateCommissioning();
    } else {
      const exposedFabrics = {};
      fabrics.forEach(
        ({ fabricIndex, externalInformation }) => exposedFabrics[fabricIndex] = externalInformation
      );
      this.state.fabrics = exposedFabrics;
    }
  }
  #initializeNode() {
    this.state.commissioned = !!this.agent.get(import_OperationalCredentialsBehavior.OperationalCredentialsBehavior).state.commissionedFabrics;
    this.endpoint.lifecycle.initialized.emit(this.state.commissioned);
  }
}
((CommissioningBehavior2) => {
  class Internal {
    unregisterFailsafeListener = void 0;
  }
  CommissioningBehavior2.Internal = Internal;
  class State {
    commissioned = false;
    fabrics = {};
    passcode = -1;
    discriminator = -1;
    flowType = import_types.CommissioningFlowType.Standard;
    additionalBleAdvertisementData = void 0;
    pairingCodes = {};
    [import_Val.Val.properties](endpoint) {
      return {
        get pairingCodes() {
          return CommissioningBehavior2.pairingCodesFor(endpoint);
        }
      };
    }
  }
  CommissioningBehavior2.State = State;
  class Events extends import_general.EventEmitter {
    commissioned = (0, import_general.Observable)();
    decommissioned = (0, import_general.Observable)();
    fabricsChanged = (0, import_general.Observable)();
  }
  CommissioningBehavior2.Events = Events;
})(CommissioningBehavior || (CommissioningBehavior = {}));
//# sourceMappingURL=CommissioningBehavior.js.map
