"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 ParallelPaseDiscovery_exports = {};
__export(ParallelPaseDiscovery_exports, {
  ParallelPaseDiscovery: () => ParallelPaseDiscovery
});
module.exports = __toCommonJS(ParallelPaseDiscovery_exports);
var import_general = require("@matter/general");
var import_protocol = require("@matter/protocol");
var import_Discovery = require("./Discovery.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("ParallelPaseDiscovery");
const CROSS_DEVICE_STAGGER_DELAY = (0, import_general.Seconds)(1);
class ParallelPaseDiscovery extends import_Discovery.Discovery {
  #paseWon = false;
  #pending = /* @__PURE__ */ new Set();
  #abort = new AbortController();
  #winner;
  #winnerAttempt;
  #winnerError;
  #extractWinner;
  #attemptErrors = new Array();
  #attemptCount = 0;
  #startedCount = 0;
  get abortSignal() {
    return this.#abort.signal;
  }
  get paseWon() {
    return this.#paseWon;
  }
  /**
   * Registers a parallel PASE attempt for the given node.
   *
   * @param factory Produces the attempt promise.  Receives a {@code winOnPase} callback that the implementation
   *   passes as the {@code continueXxx} option to the underlying PASE/commissioning call.  When invoked, it
   *   performs the race-gate logic and returns true if this attempt wins, false if another already won.
   * @param extractWinner Extracts the winner value from the settled attempt result.  Called only for the
   *   winning attempt after its promise resolves.
   */
  registerAttempt(factory, extractWinner) {
    let attempt;
    let isWinner = false;
    const winOnPase = () => {
      if (this.#paseWon) {
        return false;
      }
      this.#paseWon = true;
      isWinner = true;
      this.stop();
      this.#abort.abort();
      this.#pending.delete(attempt);
      this.#winnerAttempt = attempt;
      this.#extractWinner = extractWinner;
      return true;
    };
    const attemptIndex = this.#attemptCount++;
    const stagger = (0, import_general.Millis)(attemptIndex * CROSS_DEVICE_STAGGER_DELAY);
    const startFactory = () => {
      this.#startedCount++;
      return factory(winOnPase);
    };
    attempt = (stagger > 0 ? import_general.Abort.sleep("PASE stagger", this.#abort.signal, stagger).then(() => {
      if (!this.#abort.signal.aborted) {
        return startFactory();
      }
    }) : Promise.resolve(startFactory())).catch((error) => {
      if (isWinner) {
        this.#winnerError = error;
        return void 0;
      }
      if ((0, import_general.causedBy)(error, import_general.CanceledError)) {
        logger.debug("Canceled parallel commissioning attempt:", import_general.Diagnostic.errorMessage(error));
      } else if ((0, import_general.causedBy)(error, import_protocol.CommissioningError, import_protocol.PeerCommunicationError)) {
        this.#attemptErrors.push(error);
        logger.debug("Failed parallel commissioning attempt:", import_general.Diagnostic.errorMessage(error));
      } else {
        this.#attemptErrors.push(error);
        logger.warn("Unexpected error from parallel commissioning attempt:", error);
      }
      return void 0;
    }).finally(() => {
      this.#pending.delete(attempt);
    });
    this.#pending.add(attempt);
  }
  async onComplete() {
    if (!this.#paseWon) {
      this.#abort.abort();
    }
    try {
      if (this.#winnerAttempt !== void 0) {
        const result = await this.#winnerAttempt;
        if (this.#winnerError !== void 0) {
          throw this.#winnerError;
        }
        this.#winner = this.#extractWinner(result);
      }
    } finally {
      await import_general.MatterAggregateError.allSettled([...this.#pending], this.cleanupLabel).catch((error) => {
        logger.warn("Unexpected error during parallel attempt cleanup:", error);
      });
    }
    if (this.#winner === void 0) {
      let detail;
      if (this.#attemptCount === 0) {
        detail = "No commissionable device was discovered";
      } else if (this.#attemptErrors.length > 0) {
        detail = `${this.failureMessage} (${this.#attemptErrors.length} of ${this.#startedCount} started attempt(s) failed, ${this.#attemptCount} discovered)`;
      } else {
        detail = `${this.failureMessage} (${this.#startedCount} attempt(s) started of ${this.#attemptCount} discovered, all canceled or timed out)`;
      }
      const message = `${this} failed: ${detail}`;
      if (this.#attemptErrors.length > 0) {
        throw new import_DiscoveryError.DiscoveryAggregateError(this.#attemptErrors, message);
      }
      throw new import_DiscoveryError.DiscoveryError(message);
    }
    return this.#winner;
  }
}
//# sourceMappingURL=ParallelPaseDiscovery.js.map
