import { init_write_file_atomic, writeTextAtomic } from "../../../infra/write-file-atomic.js";
import { approveChannelPairing, approveChannelPairingBySender, dismissChannelPairingPending, listChannelPairingState, listChannelPairingSummary, revokeChannelPairingPaired } from "../../../channels/pairing/pairing-service.js";
import { mergeDistinctSenderIds } from "../../../channels/pairing/preseed-allow-from.js";
import { readFileSync } from "node:fs";
//#region src/gateway/hono/routes/channels.ts
init_write_file_atomic();
const feishuSetupSessions = /* @__PURE__ */ new Map();
async function startFeishuSetupPolling(sessionKey, service) {
	const session = feishuSetupSessions.get(sessionKey);
	if (!session) return;
	const { pollAppRegistration } = await import("../../../../extensions/feishu/src/auth/app-registration.js");
	session.phase = "polling";
	const outcome = await pollAppRegistration({
		deviceCode: session.deviceCode,
		intervalSec: session.intervalSec,
		expireInSec: session.expireInSec,
		initialDomain: session.domain
	});
	if (outcome.status === "success") {
		session.phase = "done";
		session.result = outcome.result;
		try {
			const configPath = service.getHealth().configPath;
			const raw = readFileSync(configPath, "utf8");
			const config = JSON.parse(raw);
			const existingFeishu = config.channels?.feishu ?? {};
			const dmPolicy = typeof existingFeishu.dmPolicy === "string" && existingFeishu.dmPolicy.trim() ? existingFeishu.dmPolicy : "open";
			const preseedOpenId = outcome.result.openId?.trim();
			const allowFrom = mergeDistinctSenderIds(existingFeishu.allowFrom, preseedOpenId ? [preseedOpenId] : []);
			config.channels = {
				...config.channels,
				feishu: {
					...existingFeishu,
					enabled: true,
					appId: outcome.result.appId,
					appSecret: outcome.result.appSecret,
					domain: outcome.result.domain,
					connectionMode: existingFeishu.connectionMode || "websocket",
					dmPolicy,
					allowFrom
				}
			};
			await writeTextAtomic(configPath, `${JSON.stringify(config, null, 2)}\n`);
			await service.afterFeishuCredentialsPersisted();
		} catch {}
	} else {
		session.phase = "error";
		session.error = outcome.status === "access_denied" ? "User denied authorization." : outcome.status === "expired" ? "Session expired." : outcome.status === "timeout" ? "Scan timed out." : "message" in outcome ? outcome.message : "Unknown error.";
	}
	setTimeout(() => feishuSetupSessions.delete(sessionKey), 3e4);
}
const PAIRING_CHANNELS = new Set([
	"telegram",
	"feishu",
	"weixin"
]);
function parsePairingChannel(raw) {
	const ch = (raw ?? "").trim().toLowerCase();
	return PAIRING_CHANNELS.has(ch) ? ch : null;
}
function registerChannelRoutes(authenticated, deps) {
	const { service, strictRateLimitMiddleware } = deps;
	authenticated.get("/api/channels/status", (c) => {
		const channels = service.getChannelsStatus();
		return c.json({
			ok: true,
			payload: { channels }
		});
	});
	authenticated.get("/api/channels/meta", (c) => {
		const channels = service.getChannelsHubMeta();
		return c.json({
			ok: true,
			payload: { channels }
		});
	});
	authenticated.post("/api/channels/weixin/login/start", strictRateLimitMiddleware, async (c) => {
		const body = await c.req.json().catch(() => ({}));
		const account = body && typeof body === "object" && typeof body.account === "string" ? body.account.trim() || void 0 : void 0;
		const rawTimeout = body && typeof body === "object" ? body.timeoutMs : void 0;
		const timeoutMs = typeof rawTimeout === "number" && Number.isFinite(rawTimeout) ? Math.max(6e4, rawTimeout) : void 0;
		const { startWeixinGatewayQrLogin } = await import("../../../channels/weixin/index.js");
		const result = await startWeixinGatewayQrLogin({
			configPath: service.getHealth().configPath,
			account,
			timeoutMs,
			onPersisted: async (r) => {
				if (r.ok) await service.afterWeixinCredentialsPersisted();
			}
		});
		if (result.ok === false) return c.json({
			ok: false,
			error: {
				code: "WEIXIN_LOGIN_FAILED",
				message: result.message
			}
		}, 400);
		return c.json({
			ok: true,
			payload: {
				sessionKey: result.sessionKey,
				qrcodeUrl: result.qrcodeUrl
			}
		});
	});
	authenticated.get("/api/channels/weixin/login/:sessionKey", async (c) => {
		const sessionKey = c.req.param("sessionKey")?.trim() ?? "";
		if (!sessionKey) return c.json({
			ok: false,
			error: {
				code: "BAD_REQUEST",
				message: "Missing sessionKey"
			}
		}, 400);
		const { getWeixinGatewayQrLoginStatus } = await import("../../../channels/weixin/index.js");
		const status = getWeixinGatewayQrLoginStatus(sessionKey);
		return c.json({
			ok: true,
			payload: { status }
		});
	});
	authenticated.post("/api/channels/feishu/setup/start", strictRateLimitMiddleware, async (c) => {
		const body = await c.req.json().catch(() => ({}));
		const domain = (body && typeof body === "object" && typeof body.domain === "string" ? body.domain.trim().toLowerCase() : "") === "lark" ? "lark" : "feishu";
		const { initAppRegistration, beginAppRegistration } = await import("../../../../extensions/feishu/src/auth/app-registration.js");
		if (!await initAppRegistration(domain)) return c.json({
			ok: false,
			error: {
				code: "FEISHU_SCAN_NOT_SUPPORTED",
				message: "Scan-to-create is not available."
			}
		}, 400);
		const begin = await beginAppRegistration(domain);
		const sessionKey = `feishu-setup-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
		feishuSetupSessions.set(sessionKey, {
			deviceCode: begin.deviceCode,
			domain,
			intervalSec: begin.intervalSec,
			expireInSec: begin.expireInSec,
			createdAt: Date.now(),
			phase: "idle"
		});
		startFeishuSetupPolling(sessionKey, service);
		return c.json({
			ok: true,
			payload: {
				sessionKey,
				qrUrl: begin.qrUrl
			}
		});
	});
	authenticated.get("/api/channels/feishu/setup/:sessionKey", async (c) => {
		const sessionKey = c.req.param("sessionKey")?.trim() ?? "";
		if (!sessionKey) return c.json({
			ok: false,
			error: {
				code: "BAD_REQUEST",
				message: "Missing sessionKey"
			}
		}, 400);
		const session = feishuSetupSessions.get(sessionKey);
		if (!session) return c.json({
			ok: true,
			payload: { status: {
				phase: "unknown",
				message: "Session not found or expired."
			} }
		});
		if (session.phase === "done" && session.result) return c.json({
			ok: true,
			payload: { status: {
				phase: "done",
				ok: true,
				appId: session.result.appId,
				domain: session.result.domain,
				openId: session.result.openId
			} }
		});
		if (session.phase === "error") return c.json({
			ok: true,
			payload: { status: {
				phase: "done",
				ok: false,
				message: session.error ?? "Setup failed."
			} }
		});
		return c.json({
			ok: true,
			payload: { status: { phase: "polling" } }
		});
	});
	authenticated.get("/api/channels/pairing", (c) => {
		const channel = parsePairingChannel(c.req.query("channel"));
		if (!channel) return c.json({
			ok: false,
			error: {
				code: "BAD_REQUEST",
				message: "Query param channel is required (telegram|feishu|weixin)."
			}
		}, 400);
		const accountId = c.req.query("account")?.trim() || "default";
		const config = service.currentConfig;
		const state = listChannelPairingState({
			channel,
			accountId,
			config
		});
		return c.json({
			ok: true,
			payload: state
		});
	});
	authenticated.post("/api/channels/pairing/approve", strictRateLimitMiddleware, async (c) => {
		const body = await c.req.json().catch(() => ({}));
		const channel = body && typeof body === "object" && typeof body.channel === "string" ? parsePairingChannel(body.channel) : null;
		if (!channel) return c.json({
			ok: false,
			error: {
				code: "BAD_REQUEST",
				message: "Body field channel is required (telegram|feishu|weixin)."
			}
		}, 400);
		const accountRaw = body && typeof body === "object" && typeof body.accountId === "string" ? body.accountId.trim() : "default";
		const code = body && typeof body === "object" && typeof body.code === "string" ? body.code.trim() : "";
		const result = approveChannelPairing({
			channel,
			accountId: accountRaw || "default",
			code
		});
		if (result.ok === false) return c.json({
			ok: false,
			error: {
				code: "PAIRING_INVALID",
				message: result.error
			}
		}, 400);
		return c.json({
			ok: true,
			payload: {
				senderId: result.senderId,
				alreadyPaired: result.alreadyPaired
			}
		});
	});
	authenticated.get("/api/channels/pairing/summary", (c) => {
		const config = service.currentConfig;
		const summary = listChannelPairingSummary(config);
		return c.json({
			ok: true,
			payload: { summary }
		});
	});
	authenticated.post("/api/channels/pairing/approve-sender", strictRateLimitMiddleware, async (c) => {
		const body = await c.req.json().catch(() => ({}));
		const channel = body && typeof body === "object" && typeof body.channel === "string" ? parsePairingChannel(body.channel) : null;
		if (!channel) return c.json({
			ok: false,
			error: {
				code: "BAD_REQUEST",
				message: "Body field channel is required (telegram|feishu|weixin)."
			}
		}, 400);
		const accountRaw = body && typeof body === "object" && typeof body.accountId === "string" ? body.accountId.trim() : "default";
		const senderId = body && typeof body === "object" && typeof body.senderId === "string" ? body.senderId.trim() : "";
		const result = approveChannelPairingBySender({
			channel,
			accountId: accountRaw || "default",
			senderId
		});
		if (result.ok === false) return c.json({
			ok: false,
			error: {
				code: "PAIRING_INVALID",
				message: result.error
			}
		}, 400);
		return c.json({
			ok: true,
			payload: {
				senderId: result.senderId,
				alreadyPaired: result.alreadyPaired
			}
		});
	});
	authenticated.delete("/api/channels/pairing/paired", strictRateLimitMiddleware, async (c) => {
		const body = await c.req.json().catch(() => ({}));
		const channel = body && typeof body === "object" && typeof body.channel === "string" ? parsePairingChannel(body.channel) : null;
		if (!channel) return c.json({
			ok: false,
			error: {
				code: "BAD_REQUEST",
				message: "Body field channel is required (telegram|feishu|weixin)."
			}
		}, 400);
		const accountRaw = body && typeof body === "object" && typeof body.accountId === "string" ? body.accountId.trim() : "default";
		const senderId = body && typeof body === "object" && typeof body.senderId === "string" ? body.senderId.trim() : "";
		const result = revokeChannelPairingPaired({
			channel,
			accountId: accountRaw || "default",
			senderId
		});
		if (result.ok === false) return c.json({
			ok: false,
			error: {
				code: "BAD_REQUEST",
				message: result.error
			}
		}, 400);
		return c.json({
			ok: true,
			payload: { changed: result.changed }
		});
	});
	authenticated.delete("/api/channels/pairing/pending", strictRateLimitMiddleware, async (c) => {
		const body = await c.req.json().catch(() => ({}));
		const channel = body && typeof body === "object" && typeof body.channel === "string" ? parsePairingChannel(body.channel) : null;
		if (!channel) return c.json({
			ok: false,
			error: {
				code: "BAD_REQUEST",
				message: "Body field channel is required (telegram|feishu|weixin)."
			}
		}, 400);
		const accountRaw = body && typeof body === "object" && typeof body.accountId === "string" ? body.accountId.trim() : "default";
		const senderId = body && typeof body === "object" && typeof body.senderId === "string" ? body.senderId.trim() : "";
		const result = dismissChannelPairingPending({
			channel,
			accountId: accountRaw || "default",
			senderId
		});
		if (result.ok === false) return c.json({
			ok: false,
			error: {
				code: "PAIRING_INVALID",
				message: result.error
			}
		}, 400);
		return c.json({
			ok: true,
			payload: { senderId: result.senderId }
		});
	});
}
//#endregion
export { registerChannelRoutes };

//# sourceMappingURL=channels.js.map