import { logger } from "../util/logger.js";
import { redactBody } from "../util/redact.js";
import { getUpdates } from "../api/api.js";
import { WeixinConfigManager } from "../api/config-cache.js";
import { getRemainingPauseMs, pauseSession } from "../api/session-guard.js";
import { processWeixinInboundMessage } from "../messaging/process-message.js";
import { setWeixinTypingTicket } from "../messaging/typing-ticket-store.js";
import { getSyncBufFilePath, loadGetUpdatesBuf, saveGetUpdatesBuf } from "../storage/sync-buf.js";
//#region extensions/weixin/src/monitor/monitor.ts
const DEFAULT_LONG_POLL_TIMEOUT_MS = 35e3;
const MAX_CONSECUTIVE_FAILURES = 3;
const BACKOFF_DELAY_MS = 3e4;
const RETRY_DELAY_MS = 2e3;
/**
* Long-poll loop: getUpdates → process inbound.
*/
async function monitorWeixinProvider(opts) {
	const { account, config, bus, abortSignal, longPollTimeoutMs } = opts;
	const accountId = account.accountId;
	const baseUrl = account.baseUrl;
	const cdnBaseUrl = account.cdnBaseUrl;
	const token = account.token;
	const routeTag = account.routeTag;
	const aLog = logger;
	const syncFilePath = getSyncBufFilePath(accountId);
	aLog.debug(`syncFilePath: ${syncFilePath}`);
	const previousGetUpdatesBuf = loadGetUpdatesBuf(syncFilePath);
	let getUpdatesBuf = previousGetUpdatesBuf ?? "";
	if (previousGetUpdatesBuf) aLog.debug(`Using previous get_updates_buf (${getUpdatesBuf.length} bytes)`);
	else aLog.info(`No previous get_updates_buf found, starting fresh`);
	const configLog = (msg) => {
		aLog.debug(msg);
	};
	const configManager = new WeixinConfigManager({
		baseUrl,
		token,
		routeTag
	}, configLog);
	let nextTimeoutMs = longPollTimeoutMs ?? DEFAULT_LONG_POLL_TIMEOUT_MS;
	let consecutiveFailures = 0;
	let lastHealthCheck = Date.now();
	aLog.info(`Weixin monitor started: baseUrl=${baseUrl} account=${accountId} timeoutMs=${nextTimeoutMs}`);
	while (!abortSignal?.aborted) try {
		aLog.debug(`getUpdates: get_updates_buf=${getUpdatesBuf.substring(0, 50)}..., timeoutMs=${nextTimeoutMs}`);
		const resp = await getUpdates({
			baseUrl,
			token,
			get_updates_buf: getUpdatesBuf,
			timeoutMs: nextTimeoutMs,
			routeTag,
			abortSignal
		});
		aLog.debug(`getUpdates response: ret=${resp.ret}, msgs=${resp.msgs?.length ?? 0}, get_updates_buf_length=${resp.get_updates_buf?.length ?? 0}`);
		if (resp.longpolling_timeout_ms != null && resp.longpolling_timeout_ms > 0) {
			nextTimeoutMs = resp.longpolling_timeout_ms;
			aLog.debug(`Updated next poll timeout: ${nextTimeoutMs}ms`);
		}
		if (resp.ret !== void 0 && resp.ret !== 0 || resp.errcode !== void 0 && resp.errcode !== 0) {
			if (resp.errcode === -14 || resp.ret === -14) {
				pauseSession(accountId);
				const pauseMs = getRemainingPauseMs(accountId);
				aLog.error(`getUpdates: session expired (errcode=${resp.errcode} ret=${resp.ret}), pausing all requests for ${Math.ceil(pauseMs / 6e4)} min`);
				consecutiveFailures = 0;
				await sleep(pauseMs, abortSignal);
				continue;
			}
			consecutiveFailures += 1;
			aLog.error(`getUpdates failed: ret=${resp.ret} errcode=${resp.errcode} errmsg=${resp.errmsg ?? ""} response=${redactBody(JSON.stringify(resp))}`);
			if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
				aLog.error(`getUpdates: ${MAX_CONSECUTIVE_FAILURES} consecutive failures, backing off 30s`);
				consecutiveFailures = 0;
				await sleep(BACKOFF_DELAY_MS, abortSignal);
			} else await sleep(RETRY_DELAY_MS, abortSignal);
			continue;
		}
		consecutiveFailures = 0;
		if (resp.get_updates_buf != null && resp.get_updates_buf !== "") {
			saveGetUpdatesBuf(syncFilePath, resp.get_updates_buf);
			getUpdatesBuf = resp.get_updates_buf;
			aLog.debug(`Saved new get_updates_buf (${getUpdatesBuf.length} bytes)`);
		}
		const list = resp.msgs ?? [];
		for (const full of list) {
			aLog.info(`inbound message: from=${full.from_user_id} types=${full.item_list?.map((i) => i.type).join(",") ?? "none"}`);
			const fromUserId = full.from_user_id ?? "";
			setWeixinTypingTicket(accountId, fromUserId, (await configManager.getForUser(fromUserId, full.context_token)).typingTicket);
			await processWeixinInboundMessage(full, {
				accountId,
				account,
				config,
				bus,
				baseUrl,
				cdnBaseUrl,
				token,
				routeTag
			});
		}
	} catch (err) {
		if (abortSignal?.aborted) {
			aLog.info(`Weixin monitor stopped (aborted)`);
			return;
		}
		consecutiveFailures += 1;
		const e = err;
		const cause = e && typeof e === "object" ? e.cause : void 0;
		const causeText = cause ? ` cause=${cause.name ?? ""}${cause.code ? `(${cause.code})` : ""}:${cause.message ?? String(cause)}` : "";
		aLog.error(`getUpdates error: ${String(err)}${causeText}, stack=${err.stack ?? ""}`);
		if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
			aLog.error(`getUpdates: ${MAX_CONSECUTIVE_FAILURES} consecutive failures, backing off 30s`);
			consecutiveFailures = 0;
			await sleep(3e4, abortSignal);
		} else await sleep(2e3, abortSignal);
		const HEALTH_CHECK_INTERVAL_MS = 12e4;
		const now = Date.now();
		if (now - lastHealthCheck > HEALTH_CHECK_INTERVAL_MS) {
			aLog.debug(`Health check: resetting get_updates_buf to force fresh poll`);
			getUpdatesBuf = "";
		}
		lastHealthCheck = now;
	}
	aLog.info(`Weixin monitor ended`);
}
function sleep(ms, signal) {
	return new Promise((resolve, reject) => {
		const t = setTimeout(resolve, ms);
		signal?.addEventListener("abort", () => {
			clearTimeout(t);
			reject(Object.assign(/* @__PURE__ */ new Error("aborted"), { name: "AbortError" }));
		}, { once: true });
	});
}
//#endregion
export { monitorWeixinProvider };

//# sourceMappingURL=monitor.js.map