import { createLogger } from "../utils/logger/index.js";
import { init_logger } from "../utils/logger.js";
import { randomBytes } from "node:crypto";
//#region src/tunnel/pairing.ts
init_logger();
const log = createLogger("TunnelPairing");
const PAIRING_TTL_MS = 5 * 6e4;
const CLEANUP_INTERVAL_MS = 6e4;
const sessions = /* @__PURE__ */ new Map();
let cleanupTimer = null;
function ensureCleanupTimer() {
	if (cleanupTimer) return;
	cleanupTimer = setInterval(() => {
		const now = Date.now();
		for (const [key, session] of sessions) if (now > session.expiresAt.getTime()) sessions.delete(key);
	}, CLEANUP_INTERVAL_MS);
	cleanupTimer.unref?.();
}
const EXCHANGE_REPLAY_MS = 6e4;
const exchangeReplayCache = /* @__PURE__ */ new Map();
function cachePairingExchange(secret, payload) {
	exchangeReplayCache.set(secret, {
		expiresAt: Date.now() + EXCHANGE_REPLAY_MS,
		payload
	});
}
function getCachedPairingExchange(secret) {
	const entry = exchangeReplayCache.get(secret);
	if (!entry) return null;
	if (Date.now() > entry.expiresAt) {
		exchangeReplayCache.delete(secret);
		return null;
	}
	return entry.payload;
}
const inflightExchanges = /* @__PURE__ */ new Map();
/**
* Consume a pairing secret once and build the exchange payload.
* Concurrent requests for the same secret share one in-flight exchange (mobile cold-start deeplink).
*/
async function exchangePairingSecretOnce(secret, buildPayload) {
	const key = secret.trim();
	if (!key) return null;
	const replay = getCachedPairingExchange(key);
	if (replay) return replay;
	let inflight = inflightExchanges.get(key);
	if (!inflight) {
		inflight = (async () => {
			const cached = getCachedPairingExchange(key);
			if (cached) return cached;
			if (!consumePairingSecret(key)) return getCachedPairingExchange(key);
			const payload = buildPayload();
			cachePairingExchange(key, payload);
			return payload;
		})();
		inflightExchanges.set(key, inflight);
	}
	try {
		return await inflight;
	} finally {
		if (inflightExchanges.get(key) === inflight) inflightExchanges.delete(key);
	}
}
function createPairingSecret() {
	ensureCleanupTimer();
	const secret = randomBytes(32).toString("base64url");
	const expiresAt = new Date(Date.now() + PAIRING_TTL_MS);
	sessions.set(secret, {
		expiresAt,
		consumed: false
	});
	log.info({ expiresAt: expiresAt.toISOString() }, "Pairing session created");
	return {
		secret,
		expiresAt
	};
}
function consumePairingSecret(secret) {
	if (!secret.trim()) return false;
	const session = sessions.get(secret);
	if (!session) return false;
	if (session.consumed) return false;
	if (Date.now() > session.expiresAt.getTime()) {
		sessions.delete(secret);
		return false;
	}
	session.consumed = true;
	sessions.delete(secret);
	log.info("Pairing session consumed");
	return true;
}
/** @internal */
function resetPairingSessionsForTests() {
	sessions.clear();
	exchangeReplayCache.clear();
	inflightExchanges.clear();
	if (cleanupTimer) {
		clearInterval(cleanupTimer);
		cleanupTimer = null;
	}
}
//#endregion
export { cachePairingExchange, consumePairingSecret, createPairingSecret, exchangePairingSecretOnce, getCachedPairingExchange, resetPairingSessionsForTests };

//# sourceMappingURL=pairing.js.map