"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 Discovery_exports = {};
__export(Discovery_exports, {
  Discovery: () => Discovery
});
module.exports = __toCommonJS(Discovery_exports);
var import_ClientNodeFactory = require("#node/client/ClientNodeFactory.js");
var import_general = require("@matter/general");
var import_protocol = require("@matter/protocol");
var import_ControllerBehavior = require("../ControllerBehavior.js");
var import_ActiveDiscoveries = require("./ActiveDiscoveries.js");
var import_DiscoveryError = require("./DiscoveryError.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const logger = import_general.Logger.get("Discovery");
class Discovery extends import_general.CancelablePromise {
  #abortReason;
  #isStopped = false;
  #stopDiscovery;
  #owner;
  #options;
  #resolve;
  #reject;
  #settled;
  #resolveSettled;
  constructor(owner, options) {
    let resolve, reject;
    super((resolver, rejecter) => {
      resolve = resolver;
      reject = rejecter;
    });
    this.#resolve = (result) => {
      this.#isStopped = true;
      this.#owner.env.get(import_ActiveDiscoveries.ActiveDiscoveries).delete(this);
      resolve(result);
      this.#resolveSettled?.();
    };
    this.#reject = (cause) => {
      this.#isStopped = true;
      this.#owner.env.get(import_ActiveDiscoveries.ActiveDiscoveries).delete(this);
      reject(cause);
      this.#resolveSettled?.();
    };
    owner.env.get(import_ActiveDiscoveries.ActiveDiscoveries).add(this);
    this.#owner = owner;
    this.#options = options ?? {};
    queueMicrotask(this.#initializeController.bind(this));
  }
  get settled() {
    if (this.#settled === void 0) {
      this.#settled = new Promise((resolve) => this.#resolveSettled = resolve);
    }
    return this.#settled;
  }
  get owner() {
    return this.#owner;
  }
  /**
   * Terminate discovery.
   *
   * This will not abort node initialization but it will terminate any active discoveries.  The discovery result will
   * be the same as if the discovery had timed out.
   *
   * To abort the operation due to error use {@link cancel}.
   */
  stop() {
    if (this.#isStopped) {
      return;
    }
    this.#isStopped = true;
    this.#stopDiscovery?.();
  }
  toString() {
    const description = this.#description;
    if (description === void 0) {
      return "node discovery";
    }
    return `discovery of ${description}`;
  }
  get #description() {
    if ("instanceId" in this.#options) {
      return `node instance ${this.#options.instanceId}`;
    }
    if ("longDiscriminator" in this.#options) {
      return `node with discriminator ${this.#options.longDiscriminator}`;
    }
    if ("shortDiscriminator" in this.#options) {
      return `node with discriminator ${this.#options.shortDiscriminator}`;
    }
    if ("productId" in this.#options && this.#options.productId !== void 0) {
      if ("vendorId" in this.#options) {
        return `product ${this.#options.productId} from vendor ${this.#options.vendorId}`;
      }
      return `product ${this.#options.productId}`;
    }
    if ("vendorId" in this.#options) {
      return `node from vendor ${this.#options.vendorId}`;
    }
    if ("deviceType" in this.#options) {
      return `node with device type ${this.#options.deviceType}`;
    }
    return "node discovery";
  }
  onCancel(reason) {
    this.#abortReason = reason;
    this.stop();
  }
  /**
   * Step 1 - ensure node is initialized as a controller
   */
  #initializeController() {
    let controllerInitialized;
    try {
      this.#owner.behaviors.require(import_ControllerBehavior.ControllerBehavior);
      controllerInitialized = this.#owner.act((agent) => agent.load(import_ControllerBehavior.ControllerBehavior));
    } catch (e) {
      this.#reject(e);
      return;
    }
    if (import_general.MaybePromise.is(controllerInitialized)) {
      controllerInitialized.then(this.#startNode.bind(this), this.#reject);
      return;
    }
    this.#startNode();
  }
  /**
   * Step 2 - ensure node is online
   */
  #startNode() {
    if (this.#isStopped) {
      this.#afterDiscovery();
      return;
    }
    if (this.#owner.lifecycle.isOnline) {
      this.#performDiscovery();
      return;
    }
    this.#owner.start().then(this.#performDiscovery.bind(this), this.#reject);
  }
  /**
   * Step 3 - perform actual discovery
   */
  #performDiscovery() {
    if (this.#isStopped) {
      this.#afterDiscovery();
      return;
    }
    const description = this.#description;
    if (description === void 0) {
      logger.info("Initiating", import_general.Diagnostic.strong("node discovery"));
    } else {
      logger.info("Initiating discovery of", import_general.Diagnostic.strong(description));
    }
    const scanners = this.#owner.env.get(import_protocol.ScannerSet);
    const { timeout: _, scannerFilter, id, ...identifier } = this.#options;
    const factory = this.#owner.env.get(import_ClientNodeFactory.ClientNodeFactory);
    const promises = new Array();
    const cancelSignal = new Promise((resolve) => this.#stopDiscovery = resolve);
    for (const scanner of scanners) {
      if (scannerFilter && !scannerFilter(scanner)) continue;
      promises.push(
        scanner.findCommissionableDevicesContinuously(
          identifier,
          (descriptor) => {
            let node = factory.find(descriptor);
            if (node?.lifecycle.isCommissioned) {
              node = void 0;
            }
            if (node) {
              const reusedNode = node;
              import_general.MaybePromise.then(
                node.act(async (agent) => {
                  const { transaction } = agent.context;
                  await transaction.addResources(agent.commissioning);
                  await transaction.begin();
                  agent.commissioning.descriptor = descriptor;
                }),
                () => this.onDiscovered(reusedNode),
                () => {
                }
              );
            } else {
              node = factory.create({
                id,
                environment: this.#owner.env,
                commissioning: { descriptor }
              });
              const newNode = node;
              Promise.resolve(newNode.construction.ready).then(
                () => this.onDiscovered(newNode),
                () => {
                }
              );
            }
          },
          void 0,
          cancelSignal
        )
      );
    }
    let promise = import_DiscoveryError.DiscoveryAggregateError.allSettled(promises, `${this} failed`);
    if (this.#options.timeout !== void 0) {
      promise = (0, import_general.withTimeout)(this.#options.timeout, promise, this.stop.bind(this));
    }
    promise.then(this.#afterDiscovery.bind(this)).catch(this.#reject);
  }
  /**
   * Step 4 - invoke completion callback
   */
  #afterDiscovery() {
    let result;
    if (this.#abortReason) {
      this.#reject(this.#abortReason);
      return;
    }
    try {
      result = this.onComplete();
    } catch (e) {
      this.#reject(e);
      return;
    }
    if (import_general.MaybePromise.is(result)) {
      result.then(this.#resolve.bind(this), this.#reject);
      return;
    }
    this.#resolve(result);
  }
}
//# sourceMappingURL=Discovery.js.map
