/**
 * @license
 * Copyright 2022-2025 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { AdministratorCommissioningServer } from "#behaviors/administrator-commissioning";
import { BasicInformationServer } from "#behaviors/basic-information";
import { AdministratorCommissioning } from "#clusters/administrator-commissioning";
import { GeneralCommissioning } from "#clusters/general-commissioning";
import { Bytes, Diagnostic, hex, Logger, MatterFlowError, Seconds } from "#general";
import {
  assertRemoteActor,
  DeviceCommissioner,
  FabricManager,
  GroupSession,
  NodeSession,
  SecureSession,
  SessionManager
} from "#protocol";
import { GeneralCommissioningBehavior } from "./GeneralCommissioningBehavior.js";
import { ServerNodeFailsafeContext } from "./ServerNodeFailsafeContext.js";
const SuccessResponse = { errorCode: GeneralCommissioning.CommissioningError.Ok, debugText: "" };
const logger = Logger.get("GeneralCommissioningClusterHandler");
class GeneralCommissioningServer extends GeneralCommissioningBehavior {
  static lockOnInvoke = false;
  initialize() {
    const bci = this.state.basicCommissioningInfo;
    if (bci.failSafeExpiryLengthSeconds === void 0) {
      bci.failSafeExpiryLengthSeconds = 60;
    }
    if (bci.maxCumulativeFailsafeSeconds === void 0) {
      bci.maxCumulativeFailsafeSeconds = 900;
    }
    this.state.breadcrumb = 0;
    const sessionManager = this.env.get(SessionManager);
    this.reactTo(sessionManager.sessions.added, this.#handleAddedPaseSessions);
  }
  /** As required by Commissioning Flows any new PASE session needs to arm the failsafe for 60s. */
  async #handleAddedPaseSessions(session) {
    if (session.isInitiator || // Only server sessions
    !session.isPase || // Only PASE sessions
    session.fabric !== void 0) {
      return;
    }
    logger.debug(session.via, `New PASE session, arming failsafe for 60s`);
    await this.#armFailSafe({ breadcrumb: this.state.breadcrumb, expiryLengthSeconds: 60 }, session);
  }
  async #armFailSafe({ breadcrumb, expiryLengthSeconds }, session) {
    NodeSession.assert(session, "Failsafe may only be armed on a secure session");
    const commissioner = this.env.get(DeviceCommissioner);
    try {
      if (!commissioner.isFailsafeArmed && this.agent.get(AdministratorCommissioningServer).state.windowStatus !== AdministratorCommissioning.CommissioningWindowStatus.WindowNotOpen && !session.isPase) {
        throw new MatterFlowError("Cannot arm failsafe using CASE while commissioning window is opened");
      }
      if (commissioner.isFailsafeArmed) {
        await commissioner.failsafeContext.extend(
          session.fabric,
          Seconds(expiryLengthSeconds),
          this.context.exchange
        );
      } else {
        if (expiryLengthSeconds === 0) return SuccessResponse;
        const failsafe = new ServerNodeFailsafeContext(this.endpoint, {
          fabrics: this.env.get(FabricManager),
          sessions: this.env.get(SessionManager),
          expiryLength: Seconds(expiryLengthSeconds),
          maxCumulativeFailsafe: Seconds(this.state.basicCommissioningInfo.maxCumulativeFailsafeSeconds),
          session
        });
        commissioner.beginTimed(failsafe);
        await failsafe.construction;
      }
      if (commissioner.isFailsafeArmed) {
        this.state.breadcrumb = breadcrumb;
      }
    } catch (error) {
      MatterFlowError.accept(error);
      logger.debug(`Error while arming failSafe timer:`, error);
      return {
        errorCode: GeneralCommissioning.CommissioningError.BusyWithOtherAdmin,
        debugText: error.message
      };
    }
    return SuccessResponse;
  }
  armFailSafe(request) {
    assertRemoteActor(this.context);
    return this.#armFailSafe(request, this.context.session);
  }
  async setRegulatoryConfig({
    breadcrumb,
    newRegulatoryConfig,
    countryCode
  }) {
    const locationCapabilityValue = this.state.locationCapability;
    const basicInformation = this.agent.get(BasicInformationServer);
    const currentLocationCountryCode = basicInformation.state.location;
    if (currentLocationCountryCode !== countryCode) {
      if (this.state.allowCountryCodeChange === false && countryCode !== "XX") {
        return {
          errorCode: GeneralCommissioning.CommissioningError.ValueOutsideRange,
          debugText: `Country code change not allowed: ${countryCode}`
        };
      }
      if (this.state.countryCodeWhitelist !== void 0 && !this.state.countryCodeWhitelist.includes(countryCode)) {
        return {
          errorCode: GeneralCommissioning.CommissioningError.ValueOutsideRange,
          debugText: `Country code change not allowed: ${countryCode}`
        };
      }
      if (countryCode !== "XX") {
        basicInformation.state.location = countryCode;
      }
    }
    let validValues;
    switch (locationCapabilityValue) {
      case GeneralCommissioning.RegulatoryLocationType.Outdoor:
        validValues = [GeneralCommissioning.RegulatoryLocationType.Outdoor];
        break;
      case GeneralCommissioning.RegulatoryLocationType.Indoor:
        validValues = [GeneralCommissioning.RegulatoryLocationType.Indoor];
        break;
      case GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor:
        validValues = [
          GeneralCommissioning.RegulatoryLocationType.Indoor,
          GeneralCommissioning.RegulatoryLocationType.Outdoor,
          GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor
        ];
        break;
      default:
        return {
          errorCode: GeneralCommissioning.CommissioningError.ValueOutsideRange,
          debugText: `Invalid regulatory location: ${newRegulatoryConfig === GeneralCommissioning.RegulatoryLocationType.Indoor ? "Indoor" : "Outdoor"}`
        };
    }
    if (!validValues.includes(newRegulatoryConfig)) {
      return {
        errorCode: GeneralCommissioning.CommissioningError.ValueOutsideRange,
        debugText: `Invalid regulatory location: ${newRegulatoryConfig === GeneralCommissioning.RegulatoryLocationType.Indoor ? "Indoor" : "Outdoor"}`
      };
    }
    this.state.regulatoryConfig = newRegulatoryConfig;
    this.state.breadcrumb = breadcrumb;
    return SuccessResponse;
  }
  async commissioningComplete() {
    assertRemoteActor(this.context);
    const { session } = this.context;
    if (NodeSession.is(session) && session.isPase || GroupSession.is(session)) {
      return {
        errorCode: GeneralCommissioning.CommissioningError.InvalidAuthentication,
        debugText: "Command must be executed over CASE session."
      };
    }
    const fabric = session.associatedFabric;
    const commissioner = this.env.get(DeviceCommissioner);
    if (!commissioner.isFailsafeArmed) {
      return { errorCode: GeneralCommissioning.CommissioningError.NoFailSafe, debugText: "FailSafe not armed." };
    }
    const failsafeContext = commissioner.failsafeContext;
    SecureSession.assert(session, "Commissioning may only complete on a secure session");
    const timedFabric = failsafeContext.associatedFabric?.fabricIndex;
    if (fabric.fabricIndex !== timedFabric) {
      return {
        errorCode: GeneralCommissioning.CommissioningError.InvalidAuthentication,
        debugText: `Associated fabric index ${fabric.fabricIndex} does not match failsafe fabric index ${timedFabric}`
      };
    }
    await failsafeContext.completeCommission();
    this.state.breadcrumb = BigInt(0);
    logger.info(
      "Commissioned",
      Diagnostic.dict({
        fabric: `${Bytes.toHex(fabric.operationalId)} (#${fabric.fabricIndex})`,
        node: hex.fixed(fabric.nodeId, 16)
      })
    );
    return SuccessResponse;
  }
}
((GeneralCommissioningServer2) => {
  class State extends GeneralCommissioningBehavior.State {
    /**
     * Set to false to prevent the controller from changing the country code during commissioning.
     */
    allowCountryCodeChange = true;
    // Default true if not set
    /**
     * Set to an array of two-letter country codes to limit the countries the controller may assign.
     */
    countryCodeWhitelist = void 0;
  }
  GeneralCommissioningServer2.State = State;
})(GeneralCommissioningServer || (GeneralCommissioningServer = {}));
export {
  GeneralCommissioningServer
};
//# sourceMappingURL=GeneralCommissioningServer.js.map
