"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 ServerNetworkRuntime_exports = {};
__export(ServerNetworkRuntime_exports, {
  ServerNetworkRuntime: () => ServerNetworkRuntime
});
module.exports = __toCommonJS(ServerNetworkRuntime_exports);
var import_general = require("#general");
var import_TransactionalInteractionServer = require("#node/server/TransactionalInteractionServer.js");
var import_NodeStore = require("#node/storage/NodeStore.js");
var import_protocol = require("#protocol");
var import_CommissioningBehavior = require("../commissioning/CommissioningBehavior.js");
var import_ProductDescriptionServer = require("../product-description/ProductDescriptionServer.js");
var import_SessionsBehavior = require("../sessions/SessionsBehavior.js");
var import_NetworkRuntime = require("./NetworkRuntime.js");
/**
 * @license
 * Copyright 2022-2024 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
function convertNetworkEnvironmentType(type) {
  const convertedType = typeof type === "string" ? import_general.InterfaceType[type] : type;
  if (typeof convertedType !== "number" || convertedType < 1 || convertedType > 4) {
    return void 0;
  }
  return convertedType;
}
class ServerNetworkRuntime extends import_NetworkRuntime.NetworkRuntime {
  #interactionServer;
  #matterDevice;
  #mdnsBroadcaster;
  #primaryNetInterface;
  #bleBroadcaster;
  #bleTransport;
  #commissionedListener;
  get owner() {
    return super.owner;
  }
  /**
   * Access the MDNS broadcaster for the node.
   */
  get mdnsBroadcaster() {
    if (!this.#mdnsBroadcaster) {
      this.#mdnsBroadcaster = this.owner.env.get(import_protocol.MdnsService).createInstanceBroadcaster(this.owner.state.network.operationalPort);
    }
    return this.#mdnsBroadcaster;
  }
  get networkInterfaceConfiguration() {
    const interfaceConfig = this.owner.env.vars.get(
      "network.interface",
      {}
    );
    return Object.entries(interfaceConfig).map(([name, { type }]) => ({
      name,
      type: convertNetworkEnvironmentType(type)
    }));
  }
  async getNetworkInterfaces() {
    const network = this.owner.env.get(import_general.Network);
    const interfaces = await network.getNetInterfaces(this.networkInterfaceConfiguration);
    const interfaceDetails = new Array();
    for (const { name, type } of interfaces) {
      const details = await network.getIpMac(name);
      if (details !== void 0) {
        interfaceDetails.push({ name, type, ...details });
      }
    }
    return interfaceDetails;
  }
  openAdvertisementWindow() {
    if (!this.#matterDevice) {
      throw new import_general.InternalError("Server runtime device instance is missing");
    }
    return this.#matterDevice.startAnnouncement();
  }
  advertiseNow() {
    if (!this.#matterDevice) {
      throw new import_general.InternalError("Server runtime device instance is missing");
    }
    return this.#matterDevice.announce(true);
  }
  /**
   * The IPv6 {@link UdpInterface}. We create this interface independently of the server so the OS can select a port
   * before we are fully online.
   */
  async getPrimaryNetInterface() {
    if (this.#primaryNetInterface === void 0) {
      const port = this.owner.state.network.port;
      this.#primaryNetInterface = await import_general.UdpInterface.create(
        this.owner.env.get(import_general.Network),
        "udp6",
        port ? port : void 0,
        this.owner.state.network.listeningAddressIpv6
      );
      await this.owner.set({ network: { operationalPort: this.#primaryNetInterface.port } });
    }
    return this.#primaryNetInterface;
  }
  /**
   * A BLE broadcaster.
   */
  get bleBroadcaster() {
    if (this.#bleBroadcaster === void 0) {
      const bleData = this.owner.state.commissioning.additionalBleAdvertisementData;
      this.#bleBroadcaster = import_protocol.Ble.get().getBleBroadcaster(bleData);
    }
    return this.#bleBroadcaster;
  }
  /**
   * A BLE transport.
   */
  get bleTransport() {
    if (this.#bleTransport === void 0) {
      this.#bleTransport = import_protocol.Ble.get().getBlePeripheralInterface();
    }
    return this.#bleTransport;
  }
  /**
   * Add transports to the {@link MatterDevice}.
   */
  async addTransports(device) {
    device.addTransportInterface(await this.getPrimaryNetInterface());
    const netconf = this.owner.state.network;
    if (netconf.ipv4) {
      device.addTransportInterface(
        await import_general.UdpInterface.create(
          this.owner.env.get(import_general.Network),
          "udp4",
          netconf.port,
          netconf.listeningAddressIpv4
        )
      );
    }
    if (netconf.ble) {
      device.addTransportInterface(this.bleTransport);
    }
  }
  /**
   * Add broadcasters to the {@link MatterDevice}.
   */
  async addBroadcasters(device) {
    const isCommissioned = !!this.#commissionedFabrics;
    let discoveryCapabilities = this.owner.state.network.discoveryCapabilities;
    if (isCommissioned) {
      discoveryCapabilities = { onIpNetwork: true };
    }
    if (discoveryCapabilities.onIpNetwork) {
      device.addBroadcaster(this.mdnsBroadcaster);
    }
    if (discoveryCapabilities.ble) {
      device.addBroadcaster(this.bleBroadcaster);
    }
  }
  /**
   * When the first Fabric gets added we need to enable MDNS broadcasting.
   */
  enableMdnsBroadcasting() {
    const mdnsBroadcaster = this.mdnsBroadcaster;
    if (!this.#matterDevice?.hasBroadcaster(mdnsBroadcaster)) {
      this.#matterDevice?.addBroadcaster(mdnsBroadcaster);
    }
  }
  /**
   * On commission we turn off bluetooth and join the IP network if we haven't already.
   *
   * On decommission we're destroyed so don't need to handle that case.
   */
  endUncommissionedMode() {
    if (this.#bleBroadcaster) {
      this.owner.env.runtime.add(this.#removeBleBroadcaster(this.#bleBroadcaster));
      this.#bleBroadcaster = void 0;
    }
    if (this.#bleTransport) {
      this.owner.env.runtime.add(this.#removeBleTransport(this.#bleTransport));
      this.#bleTransport = void 0;
    }
  }
  async #removeBleBroadcaster(bleBroadcaster) {
    await this.#matterDevice?.deleteBroadcaster(bleBroadcaster);
    await bleBroadcaster.close();
  }
  async #removeBleTransport(bleTransport) {
    await this.#matterDevice?.deleteTransportInterface(bleTransport);
    await bleTransport.close();
  }
  /**
   * Expose the internal InteractionServer for testing.
   */
  get interactionServer() {
    if (this.#interactionServer === void 0) {
      throw new import_general.ImplementationError("Interaction server is not available yet");
    }
    return this.#interactionServer;
  }
  get #commissionedFabrics() {
    return this.owner.state.operationalCredentials.commissionedFabrics;
  }
  get operationalPort() {
    return this.#primaryNetInterface?.port ?? 0;
  }
  endCommissioning() {
    if (this.#matterDevice !== void 0) {
      return this.#matterDevice.endCommissioning();
    }
  }
  async start() {
    await this.owner.env.load(import_protocol.MdnsService);
    await this.owner.act("start-network", (agent) => agent.load(import_ProductDescriptionServer.ProductDescriptionServer));
    const { sessionStorage, fabricStorage } = this.owner.env.get(import_NodeStore.NodeStore);
    const matterDevice = await import_protocol.MatterDevice.create(
      sessionStorage,
      fabricStorage,
      () => ({
        ...this.owner.state.commissioning,
        productDescription: this.owner.state.productDescription,
        ble: !!this.owner.state.network.ble
      }),
      this.owner.state.basicInformation.capabilityMinima.caseSessionsPerFabric,
      // Internally it is "Session and Node", so we support even more
      (_fabricIndex, _fabricAction) => {
      },
      (_fabricIndex) => {
      },
      { maxPathsPerInvoke: this.owner.state.basicInformation.maxPathsPerInvoke }
    );
    this.#matterDevice = matterDevice;
    this.#interactionServer = await import_TransactionalInteractionServer.TransactionalInteractionServer.create(this.owner, matterDevice.sessionManager);
    matterDevice.addProtocolHandler(this.#interactionServer);
    matterDevice.fabricManager.events.added.on((fabric) => {
      const fabrics = this.#matterDevice?.fabricManager.getFabrics() ?? [];
      if (fabrics.length === 1 && fabrics[0].fabricIndex === fabric.fabricIndex) {
        this.enableMdnsBroadcasting();
      }
    });
    this.owner.env.set(import_protocol.SessionManager, matterDevice.sessionManager);
    this.owner.env.set(import_protocol.FabricManager, matterDevice.fabricManager);
    this.owner.env.set(import_protocol.ExchangeManager, matterDevice.exchangeManager);
    this.owner.env.set(import_protocol.DeviceCommissioner, matterDevice.commissioner);
    this.owner.env.set(import_protocol.DeviceAdvertiser, matterDevice.advertiser);
    await this.owner.act("load-sessions", (agent) => agent.load(import_SessionsBehavior.SessionsBehavior));
    this.owner.eventsOf(import_CommissioningBehavior.CommissioningBehavior).commissioned.on(() => this.endUncommissionedMode());
    await this.addTransports(matterDevice);
    await this.addBroadcasters(matterDevice);
    await this.owner.set({ network: { operationalPort: this.operationalPort } });
    await this.openAdvertisementWindow();
  }
  async stop() {
    if (this.#matterDevice) {
      this.owner.env.delete(import_protocol.SessionManager, this.#matterDevice.sessionManager);
      this.owner.env.delete(import_protocol.FabricManager, this.#matterDevice.fabricManager);
      this.owner.env.delete(import_protocol.ExchangeManager, this.#matterDevice.exchangeManager);
      this.owner.env.delete(import_protocol.DeviceCommissioner, this.#matterDevice.commissioner);
      this.owner.env.delete(import_protocol.DeviceAdvertiser, this.#matterDevice.advertiser);
      await this.#matterDevice.close();
      this.#matterDevice = void 0;
      this.#primaryNetInterface = void 0;
    }
    if (this.#primaryNetInterface) {
      await this.#primaryNetInterface.close();
      this.#primaryNetInterface = void 0;
    }
    await this.#interactionServer?.[Symbol.asyncDispose]();
    this.#interactionServer = void 0;
    if (this.#commissionedListener) {
      const commissionedListener = this.#commissionedListener;
      this.#commissionedListener = void 0;
      this.owner.eventsOf(import_CommissioningBehavior.CommissioningBehavior).commissioned.off(commissionedListener);
    }
  }
  blockNewActivity() {
    this.#interactionServer?.blockNewActivity();
  }
}
//# sourceMappingURL=ServerNetworkRuntime.js.map
