import { resolveStandardAllowFromPath, resolveStandardPairingPath, resolveWeixinAllowFromPath, resolveWeixinPairingPath } from "./paths.js";
import { readAllowFromIdsSync, removeAllowFromIdSync } from "./allow-from-file.js";
import "./pairing-constants.js";
import { PAIRING_PENDING_TTL_MS, approvePairingBySenderIdSync, approvePairingCodeSync, dismissPairingBySenderIdSync, listPendingPairingRequestsSync } from "./pairing-store.js";
import { broadcastPairingEvent } from "./pairing-events.js";
import { resolveDmPolicy } from "../security.js";
//#region src/channels/pairing/pairing-service.ts
function normalizeAccountId(accountId) {
	return (accountId ?? "default").trim().toLowerCase() || "default";
}
function resolvePairingPaths(channel, accountId) {
	const normalized = normalizeAccountId(accountId);
	if (channel === "weixin") return {
		pairingPath: resolveWeixinPairingPath(normalized),
		allowPath: resolveWeixinAllowFromPath(normalized)
	};
	return {
		pairingPath: resolveStandardPairingPath(channel, normalized),
		allowPath: resolveStandardAllowFromPath(channel, normalized)
	};
}
function requestAccountId(entry) {
	return entry.meta?.accountId?.trim().toLowerCase() || "default";
}
function toPendingView(entry) {
	const createdMs = Date.parse(entry.createdAt);
	const expiresAt = Number.isFinite(createdMs) ? new Date(createdMs + PAIRING_PENDING_TTL_MS).toISOString() : new Date(Date.now() + PAIRING_PENDING_TTL_MS).toISOString();
	const code = entry.code ?? "";
	const codeLast4 = code.length >= 4 ? code.slice(-4) : code;
	const meta = entry.meta && typeof entry.meta === "object" ? Object.fromEntries(Object.entries(entry.meta).filter(([k, v]) => k !== "accountId" && typeof v === "string" && v.trim() !== "")) : void 0;
	return {
		senderId: entry.id,
		codeLast4,
		createdAt: entry.createdAt,
		lastSeenAt: entry.lastSeenAt,
		expiresAt,
		isStale: Number.isFinite(createdMs) && Date.now() - createdMs >= 18e5,
		meta: meta && Object.keys(meta).length > 0 ? meta : void 0
	};
}
function pendingEntryStats(pending) {
	const now = Date.now();
	let stale = 0;
	for (const p of pending) {
		const created = Date.parse(p.createdAt);
		if (Number.isFinite(created) && now - created >= 18e5) stale += 1;
	}
	return {
		pending: pending.length,
		stale,
		atCapacity: pending.length >= 3
	};
}
function resolveChannelAllowFromConfig(config, channel, accountId) {
	const ch = config?.channels?.[channel];
	if (!ch || typeof ch !== "object" || Array.isArray(ch)) return [];
	const block = ch;
	const top = Array.isArray(block.allowFrom) ? block.allowFrom.map(String).filter(Boolean) : [];
	const accounts = block.accounts;
	if (accounts && typeof accounts === "object" && !Array.isArray(accounts)) {
		const acc = accounts[accountId];
		if (acc && typeof acc === "object" && !Array.isArray(acc)) {
			const fromAcc = acc.allowFrom;
			if (Array.isArray(fromAcc)) return [...new Set([...top, ...fromAcc.map(String).filter(Boolean)])];
		}
	}
	return [...new Set(top)];
}
function resolveChannelEnabled(config, channel) {
	const ch = config?.channels?.[channel];
	if (!ch || typeof ch !== "object" || Array.isArray(ch)) return false;
	return ch.enabled === true;
}
function resolveAccountEnabled(config, channel, accountId) {
	if (!resolveChannelEnabled(config, channel)) return false;
	const ch = config?.channels?.[channel];
	if (!ch || typeof ch !== "object" || Array.isArray(ch)) return true;
	const accounts = ch.accounts;
	if (accounts && typeof accounts === "object" && !Array.isArray(accounts)) {
		const acc = accounts[accountId];
		if (acc && typeof acc === "object" && !Array.isArray(acc)) return acc.enabled !== false;
	}
	return true;
}
function resolveChannelDmPolicy(config, channel, accountId) {
	const ch = config?.channels?.[channel];
	if (!ch || typeof ch !== "object" || Array.isArray(ch)) return resolveDmPolicy(void 0, channel === "weixin" ? "open" : "pairing");
	const block = ch;
	const accounts = block.accounts;
	if (accounts && typeof accounts === "object" && !Array.isArray(accounts)) {
		const acc = accounts[accountId];
		if (acc && typeof acc === "object" && !Array.isArray(acc)) {
			const accPolicy = acc.dmPolicy;
			if (accPolicy) return resolveDmPolicy(accPolicy, "pairing");
		}
	}
	const topPolicy = block.dmPolicy;
	return resolveDmPolicy(topPolicy, channel === "weixin" ? "open" : "pairing");
}
function resolvePairingAccountIds(config, channel) {
	const ch = config?.channels?.[channel];
	if (!ch || typeof ch !== "object" || Array.isArray(ch)) return channel === "telegram" ? ["default"] : [];
	const block = ch;
	const accounts = block.accounts;
	if (accounts && typeof accounts === "object" && !Array.isArray(accounts)) {
		const keys = Object.keys(accounts).sort();
		if (keys.length > 0) return keys;
	}
	if (channel === "feishu") {
		const appId = typeof block.appId === "string" ? block.appId.trim() : "";
		const appSecret = typeof block.appSecret === "string" ? block.appSecret.trim() : "";
		return appId && appSecret ? ["default"] : [];
	}
	if (channel === "telegram") return ["default"];
	return ["default"];
}
function listChannelPairingSummary(config) {
	const channels = [
		"telegram",
		"feishu",
		"weixin"
	];
	const summary = {
		telegram: {
			pending: 0,
			stale: 0,
			atCapacity: false
		},
		feishu: {
			pending: 0,
			stale: 0,
			atCapacity: false
		},
		weixin: {
			pending: 0,
			stale: 0,
			atCapacity: false
		}
	};
	for (const channel of channels) {
		if (!resolveChannelEnabled(config, channel)) continue;
		const accountIds = resolvePairingAccountIds(config, channel);
		for (const accountId of accountIds) {
			if (!resolveAccountEnabled(config, channel, accountId)) continue;
			if (resolveChannelDmPolicy(config, channel, accountId) !== "pairing") continue;
			const { pairingPath } = resolvePairingPaths(channel, accountId);
			const stats = pendingEntryStats(listPendingPairingRequestsSync(pairingPath).filter((r) => requestAccountId(r) === accountId));
			summary[channel].pending += stats.pending;
			summary[channel].stale += stats.stale;
			summary[channel].atCapacity ||= stats.atCapacity;
		}
	}
	return summary;
}
function collectPairingPendingIssues(config) {
	const issues = [];
	for (const channel of [
		"telegram",
		"feishu",
		"weixin"
	]) {
		if (!resolveChannelEnabled(config, channel)) continue;
		const accountIds = resolvePairingAccountIds(config, channel);
		for (const accountId of accountIds) {
			if (!resolveAccountEnabled(config, channel, accountId)) continue;
			if (resolveChannelDmPolicy(config, channel, accountId) !== "pairing") continue;
			const { pairingPath } = resolvePairingPaths(channel, accountId);
			const stats = pendingEntryStats(listPendingPairingRequestsSync(pairingPath).filter((r) => requestAccountId(r) === accountId));
			if (stats.pending > 0) issues.push({
				channel,
				accountId,
				...stats
			});
		}
	}
	return issues;
}
function listChannelPairingState(params) {
	const accountId = normalizeAccountId(params.accountId);
	const { pairingPath, allowPath } = resolvePairingPaths(params.channel, accountId);
	const pendingRaw = listPendingPairingRequestsSync(pairingPath).filter((r) => requestAccountId(r) === accountId);
	return {
		channel: params.channel,
		accountId,
		dmPolicy: resolveChannelDmPolicy(params.config, params.channel, accountId),
		pending: pendingRaw.map(toPendingView),
		paired: {
			fromConfig: resolveChannelAllowFromConfig(params.config, params.channel, accountId),
			fromCredentials: readAllowFromIdsSync(allowPath)
		}
	};
}
function approveChannelPairing(params) {
	const accountId = normalizeAccountId(params.accountId);
	const code = params.code.trim();
	if (!code) return {
		ok: false,
		error: "Missing pairing code."
	};
	const { pairingPath, allowPath } = resolvePairingPaths(params.channel, accountId);
	const alreadyPaired = readAllowFromIdsSync(allowPath);
	const result = approvePairingCodeSync({
		pairingFilePath: pairingPath,
		allowFromFilePath: allowPath,
		code,
		accountId
	});
	if (!result) return {
		ok: false,
		error: "Invalid or expired pairing code (or wrong account)."
	};
	broadcastPairingEvent("channels.pairing.approved", {
		channel: params.channel,
		accountId,
		senderId: result.senderId
	});
	return {
		ok: true,
		senderId: result.senderId,
		alreadyPaired: alreadyPaired.includes(result.senderId)
	};
}
function approveChannelPairingBySender(params) {
	const accountId = normalizeAccountId(params.accountId);
	const senderId = params.senderId.trim();
	if (!senderId) return {
		ok: false,
		error: "Missing sender id."
	};
	const { pairingPath, allowPath } = resolvePairingPaths(params.channel, accountId);
	const alreadyPaired = readAllowFromIdsSync(allowPath);
	const result = approvePairingBySenderIdSync({
		pairingFilePath: pairingPath,
		allowFromFilePath: allowPath,
		senderId,
		accountId
	});
	if (!result) return {
		ok: false,
		error: "No pending pairing request for this sender (or wrong account)."
	};
	broadcastPairingEvent("channels.pairing.approved", {
		channel: params.channel,
		accountId,
		senderId: result.senderId
	});
	return {
		ok: true,
		senderId: result.senderId,
		alreadyPaired: alreadyPaired.includes(result.senderId)
	};
}
function revokeChannelPairingPaired(params) {
	const accountId = normalizeAccountId(params.accountId);
	const senderId = params.senderId.trim();
	if (!senderId) return {
		ok: false,
		error: "Missing sender id."
	};
	const { allowPath } = resolvePairingPaths(params.channel, accountId);
	const { changed } = removeAllowFromIdSync(allowPath, senderId);
	if (changed) broadcastPairingEvent("channels.pairing.revoked", {
		channel: params.channel,
		accountId,
		senderId
	});
	return {
		ok: true,
		changed
	};
}
function dismissChannelPairingPending(params) {
	const accountId = normalizeAccountId(params.accountId);
	const senderId = params.senderId.trim();
	if (!senderId) return {
		ok: false,
		error: "Missing sender id."
	};
	const { pairingPath } = resolvePairingPaths(params.channel, accountId);
	const result = dismissPairingBySenderIdSync({
		pairingFilePath: pairingPath,
		senderId,
		accountId
	});
	if (!result) return {
		ok: false,
		error: "No pending pairing request for this sender (or wrong account)."
	};
	broadcastPairingEvent("channels.pairing.dismissed", {
		channel: params.channel,
		accountId,
		senderId: result.senderId
	});
	return {
		ok: true,
		senderId: result.senderId
	};
}
//#endregion
export { approveChannelPairing, approveChannelPairingBySender, collectPairingPendingIssues, dismissChannelPairingPending, listChannelPairingState, listChannelPairingSummary, revokeChannelPairingPaired };

//# sourceMappingURL=pairing-service.js.map