var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
var __typeError = (msg) => {
  throw TypeError(msg);
};
var __using = (stack, value, async) => {
  if (value != null) {
    if (typeof value !== "object" && typeof value !== "function") __typeError("Object expected");
    var dispose, inner;
    if (async) dispose = value[__knownSymbol("asyncDispose")];
    if (dispose === void 0) {
      dispose = value[__knownSymbol("dispose")];
      if (async) inner = dispose;
    }
    if (typeof dispose !== "function") __typeError("Object not disposable");
    if (inner) dispose = function() {
      try {
        inner.call(this);
      } catch (e) {
        return Promise.reject(e);
      }
    };
    stack.push([async, dispose, value]);
  } else if (async) {
    stack.push([async]);
  }
  return value;
};
var __callDispose = (stack, error, hasError) => {
  var E = typeof SuppressedError === "function" ? SuppressedError : function(e, s, m, _) {
    return _ = Error(m), _.name = "SuppressedError", _.error = e, _.suppressed = s, _;
  };
  var fail = (e) => error = hasError ? new E(e, error, "An error was suppressed during disposal") : (hasError = true, e);
  var next = (it) => {
    while (it = stack.pop()) {
      try {
        var result = it[1] && it[1].call(it[2]);
        if (it[0]) return Promise.resolve(result).then(next, (e) => (fail(e), next()));
      } catch (e) {
        fail(e);
      }
    }
    if (hasError) throw error;
  };
  return next();
};
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { PairRetransmissionLimitReachedError } from "#peer/CommissioningError.js";
import {
  Abort,
  asError,
  causedBy,
  Logger,
  MatterAggregateError,
  NetworkError,
  NoResponseTimeoutError,
  ServerAddress,
  TimeoutError,
  UnexpectedDataError
} from "@matter/general";
import { CommissioningConnectionPool } from "./CommissioningConnectionPool.js";
import { TransientPeerCommunicationError } from "./PeerCommunicationError.js";
const logger = Logger.get("CommissioningConnection");
const LOSER_CLEANUP_BUDGET_MS = 5e3;
async function CommissioningConnection(options) {
  var _stack = [];
  try {
    const abort = __using(_stack, new Abort({ timeout: options.timeout, abort: options.externalAbort }));
    const pool = new CommissioningConnectionPool(options.devices);
    let lastError;
    let lastNonRetryableError;
    const inFlight = /* @__PURE__ */ new Set();
    const pending = /* @__PURE__ */ new Set();
    const deviceAborts = /* @__PURE__ */ new Map();
    const getDeviceAbort = (deviceKey) => {
      let ac = deviceAborts.get(deviceKey);
      if (ac === void 0) {
        ac = new AbortController();
        deviceAborts.set(deviceKey, ac);
      }
      return ac;
    };
    let winner;
    const launchAttempt = (candidate) => {
      if (inFlight.has(candidate.attemptKey)) return;
      inFlight.add(candidate.attemptKey);
      const deviceAc = getDeviceAbort(candidate.deviceKey);
      const signal = AbortSignal.any([abort.signal, deviceAc.signal]);
      const p = options.establishSession(candidate.address, candidate.device, signal).then((session) => {
        if (winner !== void 0 || abort.aborted) {
          session.initiateForceClose({ cause: asError(abort.reason ?? new Error("commissioning race lost")) }).catch((e) => {
            logger.warn("Error closing losing PASE session:", asError(e));
          });
          return null;
        }
        winner = { session, discoveryData: candidate.device };
        abort.abort();
        return winner;
      }).catch((error) => {
        if (!abort.aborted && !deviceAc.signal.aborted) {
          const asErr = asError(error);
          if (causedBy(asErr, UnexpectedDataError)) {
            logger.info(`Dropping device ${candidate.device.deviceIdentifier} due to invalid credentials`);
            lastNonRetryableError = asErr;
            deviceAc.abort();
            pool.markInvalidCredentials(candidate.deviceKey);
          } else if (causedBy(asErr, NoResponseTimeoutError, TransientPeerCommunicationError, NetworkError)) {
            lastError = asErr;
            logger.warn(
              `Address ${ServerAddress.urlFor(candidate.address)} unreachable for ${candidate.device.deviceIdentifier}`
            );
          } else {
            abort.abort();
            lastNonRetryableError = asErr;
          }
        }
        return null;
      }).finally(() => {
        pending.delete(p);
        inFlight.delete(candidate.attemptKey);
      });
      pending.add(p);
    };
    for (const candidate of pool.availableCandidates(inFlight)) {
      launchAttempt(candidate);
    }
    if (pending.size > 0) {
      await abort.race(...pending);
    }
    const cleanupBudget = new Promise((resolve) => setTimeout(resolve, LOSER_CLEANUP_BUDGET_MS));
    await Promise.race([MatterAggregateError.allSettled([...pending]).catch(() => {
    }), cleanupBudget]);
    if (winner !== void 0) {
      return winner;
    }
    if (lastNonRetryableError !== void 0) {
      throw lastNonRetryableError;
    }
    if (abort.aborted && lastError === void 0) {
      const reason = abort.reason;
      if (reason !== void 0 && !(reason instanceof TimeoutError)) {
        throw reason;
      }
      throw new PairRetransmissionLimitReachedError("Failed to connect on any discovered server before timeout");
    }
    if (lastError !== void 0) {
      throw new PairRetransmissionLimitReachedError(
        `Failed to connect on any discovered server: ${lastError.message}`
      );
    }
    throw new PairRetransmissionLimitReachedError("Failed to connect on any discovered server");
  } catch (_) {
    var _error = _, _hasError = true;
  } finally {
    __callDispose(_stack, _error, _hasError);
  }
}
export {
  CommissioningConnection
};
//# sourceMappingURL=CommissioningConnection.js.map
