import { y as resolveStateDir } from "./paths-DyelItkH.js";
import { i as formatErrorMessage } from "./errors-Ca_eBjrO.js";
import { i as GATEWAY_CLIENT_NAMES, r as GATEWAY_CLIENT_MODES } from "./client-info-CcqJJIan.js";
import { n as GatewayClient, r as GatewayClientRequestError, u as loadDeviceAuthToken } from "./client-CUiG2jZ2.js";
import { o as resolveSafeTimeoutDelayMs, t as MAX_SAFE_TIMEOUT_DELAY_MS } from "./timeouts-DdTImbzl.js";
import { t as startGatewayClientWhenEventLoopReady } from "./client-start-readiness-DaX1iFFP.js";
import { i as READ_SCOPE } from "./operator-scopes-CS3xdS-V.js";
import "./method-scopes-DNS7LzT4.js";
import path from "node:path";
import { randomUUID } from "node:crypto";
//#region src/gateway/probe.ts
const MIN_PROBE_TIMEOUT_MS = 250;
const MAX_TIMER_DELAY_MS = MAX_SAFE_TIMEOUT_DELAY_MS;
const PAIRING_REQUIRED_PATTERN = /\bpairing required\b/i;
const OPERATOR_READ_SCOPE = "operator.read";
const OPERATOR_WRITE_SCOPE = "operator.write";
const OPERATOR_ADMIN_SCOPE = "operator.admin";
const DEVICE_IDENTITY_REQUIRED_CLOSE_CODE = 1008;
const DEVICE_IDENTITY_REQUIRED_CLOSE_REASON = "device identity required";
const DEVICE_REQUIRED_PROBE_FAILURE_THRESHOLD = 3;
const DEVICE_REQUIRED_PROBE_TTL_MS = 5 * 6e4;
const PROBE_CLIENT_STOP_TIMEOUT_MS = 1e3;
const deviceRequiredProbeCache = /* @__PURE__ */ new Map();
function clampProbeTimeoutMs(timeoutMs) {
	return resolveSafeTimeoutDelayMs(timeoutMs, { minMs: 250 });
}
function formatProbeCloseError(close) {
	return `gateway closed (${close.code}): ${close.reason}`;
}
function resolveDeviceRequiredProbeCacheKey(url) {
	try {
		return new URL(url).href;
	} catch {
		return url;
	}
}
function isDeviceIdentityRequiredClose(close) {
	return close?.code === DEVICE_IDENTITY_REQUIRED_CLOSE_CODE && close.reason.trim().toLowerCase() === DEVICE_IDENTITY_REQUIRED_CLOSE_REASON;
}
function hasProbeAuth(auth) {
	return Boolean(auth?.token?.trim() || auth?.password?.trim());
}
function shouldShortCircuitDeviceRequiredProbe(cacheKey, nowMs) {
	const entry = deviceRequiredProbeCache.get(cacheKey);
	if (!entry) return false;
	if (nowMs - entry.firstFailureAtMs >= DEVICE_REQUIRED_PROBE_TTL_MS) {
		deviceRequiredProbeCache.delete(cacheKey);
		return false;
	}
	return entry.failures >= DEVICE_REQUIRED_PROBE_FAILURE_THRESHOLD;
}
function noteDeviceRequiredProbeFailure(cacheKey, nowMs) {
	const existing = deviceRequiredProbeCache.get(cacheKey);
	if (!existing || nowMs - existing.firstFailureAtMs >= DEVICE_REQUIRED_PROBE_TTL_MS) {
		deviceRequiredProbeCache.set(cacheKey, {
			failures: 1,
			firstFailureAtMs: nowMs
		});
		return;
	}
	existing.failures += 1;
}
function clearDeviceRequiredProbeFailures(cacheKey) {
	deviceRequiredProbeCache.delete(cacheKey);
}
function emptyProbeAuth() {
	return {
		role: null,
		scopes: [],
		capability: "unknown"
	};
}
function emptyProbeServer() {
	return {
		version: null,
		connId: null
	};
}
function makeDeviceRequiredShortCircuitResult(url) {
	const close = {
		code: DEVICE_IDENTITY_REQUIRED_CLOSE_CODE,
		reason: DEVICE_IDENTITY_REQUIRED_CLOSE_REASON,
		hint: "probe short-circuited by recent device-required rejections"
	};
	return {
		ok: false,
		url,
		connectLatencyMs: null,
		error: formatProbeCloseError(close),
		close,
		auth: emptyProbeAuth(),
		server: emptyProbeServer(),
		health: null,
		status: null,
		presence: null,
		configSnapshot: null
	};
}
function resolveProbeAuthSummary(params) {
	const scopes = Array.isArray(params.scopes) ? params.scopes : [];
	return {
		role: params.role ?? null,
		scopes,
		capability: resolveGatewayProbeCapability({
			auth: { scopes },
			authMetadataPresent: params.authMetadataPresent,
			error: params.error,
			close: params.close,
			verifiedRead: params.verifiedRead,
			connectLatencyMs: params.connectLatencyMs
		})
	};
}
function isPairingPendingProbeFailure(params) {
	return PAIRING_REQUIRED_PATTERN.test(params.close?.reason ?? params.error ?? "");
}
function resolveGatewayProbeCapability(params) {
	if (isPairingPendingProbeFailure(params)) return "pairing_pending";
	const scopes = Array.isArray(params.auth?.scopes) ? params.auth.scopes : [];
	if (scopes.includes(OPERATOR_ADMIN_SCOPE)) return "admin_capable";
	if (scopes.includes(OPERATOR_WRITE_SCOPE)) return "write_capable";
	if (scopes.includes(OPERATOR_READ_SCOPE) || params.verifiedRead === true) return "read_only";
	if (params.connectLatencyMs != null && params.authMetadataPresent === true) return "connected_no_operator_scope";
	return "unknown";
}
async function probeGateway(opts) {
	const startedAt = Date.now();
	const instanceId = randomUUID();
	let connectLatencyMs = null;
	let connectError = null;
	let connectErrorDetails = null;
	let close = null;
	let auth = emptyProbeAuth();
	let server = emptyProbeServer();
	let authMetadataPresent = false;
	const detailLevel = opts.includeDetails === false ? "none" : opts.detailLevel ?? "full";
	const deviceIdentity = await (async () => {
		try {
			if (!URL.canParse(opts.url)) return null;
			const { loadDeviceIdentityIfPresent } = await import("./device-identity-DZAFxAiV.js");
			const stateDir = resolveStateDir(opts.env);
			const identity = loadDeviceIdentityIfPresent(path.join(stateDir, "identity", "device.json"));
			if (!identity) return null;
			return loadDeviceAuthToken({
				deviceId: identity.deviceId,
				role: "operator",
				env: opts.env
			}) ? identity : null;
		} catch {
			return null;
		}
	})();
	const cacheKey = resolveDeviceRequiredProbeCacheKey(opts.url);
	const cacheEligible = deviceIdentity == null && !hasProbeAuth(opts.auth);
	if (cacheEligible && shouldShortCircuitDeviceRequiredProbe(cacheKey, Date.now())) return makeDeviceRequiredShortCircuitResult(opts.url);
	const initialProbeTimeoutMs = clampProbeTimeoutMs(opts.timeoutMs);
	return await new Promise((resolve) => {
		let settled = false;
		let timer = null;
		const startAbort = new AbortController();
		const clearProbeTimer = () => {
			if (timer) {
				clearTimeout(timer);
				timer = null;
			}
		};
		const armProbeTimer = (onTimeout, timeoutMs = initialProbeTimeoutMs) => {
			clearProbeTimer();
			timer = setTimeout(onTimeout, resolveSafeTimeoutDelayMs(timeoutMs));
		};
		const settle = (result) => {
			if (settled) return;
			settled = true;
			startAbort.abort();
			clearProbeTimer();
			(async () => {
				try {
					await client.stopAndWait({ timeoutMs: PROBE_CLIENT_STOP_TIMEOUT_MS });
				} catch {
					client.stop();
				}
				if (result.ok) clearDeviceRequiredProbeFailures(cacheKey);
				else if (cacheEligible && isDeviceIdentityRequiredClose(result.close)) noteDeviceRequiredProbeFailure(cacheKey, Date.now());
				const { connectErrorDetails: resultConnectErrorDetails, ...rest } = result;
				resolve({
					url: opts.url,
					...rest,
					...resultConnectErrorDetails != null ? { connectErrorDetails: resultConnectErrorDetails } : {}
				});
			})();
		};
		const settleProbe = (params) => {
			settle({
				ok: params.ok,
				connectLatencyMs,
				error: params.error,
				connectErrorDetails,
				close,
				auth: resolveProbeAuthSummary({
					role: auth.role,
					scopes: auth.scopes,
					authMetadataPresent,
					error: params.error,
					close,
					verifiedRead: params.verifiedRead,
					connectLatencyMs
				}),
				server,
				health: params.health,
				status: params.status,
				presence: params.presence,
				configSnapshot: params.configSnapshot
			});
		};
		const client = new GatewayClient({
			url: opts.url,
			token: opts.auth?.token,
			password: opts.auth?.password,
			tlsFingerprint: opts.tlsFingerprint,
			preauthHandshakeTimeoutMs: opts.preauthHandshakeTimeoutMs,
			env: opts.env,
			scopes: [READ_SCOPE],
			clientName: GATEWAY_CLIENT_NAMES.CLI,
			clientVersion: "dev",
			mode: GATEWAY_CLIENT_MODES.PROBE,
			instanceId,
			deviceIdentity,
			onConnectError: (err) => {
				connectError = formatErrorMessage(err);
				connectErrorDetails = err instanceof GatewayClientRequestError ? err.details : null;
			},
			onClose: (code, reason, info) => {
				close = {
					code,
					reason
				};
				if (connectLatencyMs == null) {
					if (info?.transportValidated === true) connectLatencyMs = Date.now() - startedAt;
					settleProbe({
						ok: false,
						error: connectError || formatProbeCloseError(close),
						health: null,
						status: null,
						presence: null,
						configSnapshot: null
					});
				}
			},
			onHelloOk: (hello) => {
				(async () => {
					connectLatencyMs = Date.now() - startedAt;
					authMetadataPresent = typeof hello?.auth === "object" && hello.auth !== null;
					server = {
						version: typeof hello?.server?.version === "string" ? hello.server.version : null,
						connId: typeof hello?.server?.connId === "string" ? hello.server.connId : null
					};
					auth = resolveProbeAuthSummary({
						role: typeof hello?.auth?.role === "string" ? hello.auth.role : null,
						scopes: Array.isArray(hello?.auth?.scopes) ? hello.auth.scopes.filter((scope) => typeof scope === "string") : [],
						authMetadataPresent
					});
					if (detailLevel === "none") {
						settleProbe({
							ok: true,
							error: null,
							verifiedRead: false,
							health: null,
							status: null,
							presence: null,
							configSnapshot: null
						});
						return;
					}
					armProbeTimer(() => {
						settleProbe({
							ok: false,
							error: "timeout",
							health: null,
							status: null,
							presence: null,
							configSnapshot: null
						});
					});
					try {
						if (detailLevel === "presence") {
							const presence = await client.request("system-presence");
							settleProbe({
								ok: true,
								error: null,
								verifiedRead: true,
								health: null,
								status: null,
								presence: Array.isArray(presence) ? presence : null,
								configSnapshot: null
							});
							return;
						}
						const [health, status, presence, configSnapshot] = await Promise.all([
							client.request("health"),
							client.request("status"),
							client.request("system-presence"),
							client.request("config.get", {})
						]);
						settleProbe({
							ok: true,
							error: null,
							verifiedRead: true,
							health,
							status,
							presence: Array.isArray(presence) ? presence : null,
							configSnapshot
						});
					} catch (err) {
						settleProbe({
							ok: false,
							error: formatErrorMessage(err),
							health: null,
							status: null,
							presence: null,
							configSnapshot: null
						});
					}
				})();
			}
		});
		armProbeTimer(() => {
			settleProbe({
				ok: false,
				error: connectError ? `connect failed: ${connectError}` : "timeout",
				health: null,
				status: null,
				presence: null,
				configSnapshot: null
			});
		});
		startGatewayClientWhenEventLoopReady(client, {
			timeoutMs: initialProbeTimeoutMs,
			signal: startAbort.signal
		}).then((readiness) => {
			if (settled || readiness.ready || readiness.aborted) return;
			settleProbe({
				ok: false,
				error: "timeout",
				health: null,
				status: null,
				presence: null,
				configSnapshot: null
			});
		}).catch((err) => {
			if (settled) return;
			connectError = formatErrorMessage(err);
			settleProbe({
				ok: false,
				error: connectError,
				health: null,
				status: null,
				presence: null,
				configSnapshot: null
			});
		});
	});
}
//#endregion
export { probeGateway as a, isPairingPendingProbeFailure as i, MIN_PROBE_TIMEOUT_MS as n, resolveGatewayProbeCapability as o, clampProbeTimeoutMs as r, MAX_TIMER_DELAY_MS as t };
