import { t as createSubsystemLogger } from "./subsystem-BkJiB-Yv.js";
import { r as runCommandWithTimeout } from "./exec-CEH5Br3m.js";
import "./config-BYUi5aZZ.js";
import { n as hasBinary } from "./config-eval-CAHzuwOy.js";
import { D as resolveGogServeInvocation, E as resolveGogExecutable, T as resolveGmailHookRuntimeConfig, _ as buildGogWatchServeLogArgs, g as buildGogWatchServeArgs, i as ensureTailscaleEndpoint, v as buildGogWatchStartArgs } from "./gmail-setup-utils-B49-m-pf.js";
import { spawn } from "node:child_process";
//#region src/hooks/gmail-watcher-errors.ts
const ADDRESS_IN_USE_RE = /address already in use|EADDRINUSE/i;
/** Detect watcher startup failures caused by an occupied bind port. */
function isAddressInUseError(line) {
	return ADDRESS_IN_USE_RE.test(line);
}
//#endregion
//#region src/hooks/gmail-watcher.ts
/**
* Gmail Watcher Service
*
* Automatically starts `gog gmail watch serve` when the gateway starts,
* if hooks.gmail is configured with an account.
*/
const log = createSubsystemLogger("gmail-watcher");
let watcherProcess = null;
let renewInterval = null;
let shuttingDown = false;
let currentConfig = null;
let respawnTimeout = null;
/**
* Check if gog binary is available
*/
function isGogAvailable() {
	return hasBinary("gog");
}
/**
* Start the Gmail watch (registers with Gmail API)
*/
async function startGmailWatch(cfg, options = {}) {
	const args = [resolveGogExecutable(), ...buildGogWatchStartArgs(cfg)];
	try {
		const result = await runCommandWithTimeout(args, {
			timeoutMs: 12e4,
			signal: options.signal
		});
		if (result.code !== 0) {
			const message = result.stderr || result.stdout || "gog watch start failed";
			log.error(`watch start failed: ${message}`);
			return false;
		}
		log.info(`watch started for ${cfg.account}`);
		return true;
	} catch (err) {
		log.error(`watch start error: ${String(err)}`);
		return false;
	}
}
/**
* Spawn the gog gmail watch serve process
*/
function spawnGogServe(cfg) {
	const args = buildGogWatchServeArgs(cfg);
	log.info(`starting gog ${buildGogWatchServeLogArgs(cfg).join(" ")}`);
	let addressInUse = false;
	const invocation = resolveGogServeInvocation(args);
	const child = spawn(invocation.command, invocation.args, {
		stdio: [
			"ignore",
			"pipe",
			"pipe"
		],
		detached: false,
		windowsHide: invocation.windowsHide,
		windowsVerbatimArguments: invocation.windowsVerbatimArguments
	});
	child.stdout?.on("data", (data) => {
		const line = data.toString().trim();
		if (line) log.info(`[gog] ${line}`);
	});
	child.stderr?.on("data", (data) => {
		const line = data.toString().trim();
		if (!line) return;
		if (isAddressInUseError(line)) addressInUse = true;
		log.warn(`[gog] ${line}`);
	});
	child.on("error", (err) => {
		log.error(`gog process error: ${String(err)}`);
	});
	child.on("exit", (code, signal) => {
		if (watcherProcess !== null && watcherProcess !== child) return;
		if (shuttingDown) return;
		if (addressInUse) {
			log.warn("gog serve failed to bind (address already in use); stopping restarts. Another watcher is likely running. Set OPENCLAW_SKIP_GMAIL_WATCHER=1 or stop the other process.");
			watcherProcess = null;
			return;
		}
		log.warn(`gog exited (code=${code}, signal=${signal}); restarting in 5s`);
		watcherProcess = null;
		respawnTimeout = setTimeout(() => {
			respawnTimeout = null;
			if (shuttingDown || !currentConfig) return;
			watcherProcess = spawnGogServe(currentConfig);
		}, 5e3);
	});
	return child;
}
/**
* Send SIGTERM, escalate to SIGKILL after 3 s, and resolve on exit/close/error
* or a final 5 s timeout after SIGKILL so the caller never hangs.
*/
function settleProcess(proc) {
	return new Promise((resolve) => {
		let settled = false;
		const settle = () => {
			if (settled) return;
			settled = true;
			clearTimeout(escalation);
			clearTimeout(finalTimeout);
			resolve();
		};
		proc.on("exit", settle);
		proc.on("close", settle);
		proc.on("error", settle);
		proc.kill("SIGTERM");
		const escalation = setTimeout(() => {
			try {
				proc.kill("SIGKILL");
			} catch {}
		}, 3e3);
		const finalTimeout = setTimeout(() => {
			if (!settled) {
				log.warn("gog process did not exit after SIGKILL; giving up");
				settle();
			}
		}, 8e3);
	});
}
function cancelledGmailWatcherStart(expectedConfig) {
	if (currentConfig === expectedConfig) currentConfig = null;
	return {
		started: false,
		reason: "startup cancelled"
	};
}
function isGmailWatcherStartCancelled(options) {
	return options.signal?.aborted === true || options.isCancelled?.() === true;
}
function createGmailWatcherCancellation(options) {
	if (!options.signal && !options.isCancelled) return {
		dispose: () => {},
		isCancelled: () => false
	};
	const abortController = new AbortController();
	const abort = () => {
		if (!abortController.signal.aborted) abortController.abort();
	};
	const onAbort = () => abort();
	options.signal?.addEventListener("abort", onAbort, { once: true });
	let cancelPoll = null;
	if (options.isCancelled) {
		cancelPoll = setInterval(() => {
			if (options.isCancelled?.()) abort();
		}, 100);
		cancelPoll.unref?.();
	}
	if (isGmailWatcherStartCancelled(options)) abort();
	return {
		dispose: () => {
			if (cancelPoll) {
				clearInterval(cancelPoll);
				cancelPoll = null;
			}
			options.signal?.removeEventListener("abort", onAbort);
		},
		isCancelled: () => abortController.signal.aborted || isGmailWatcherStartCancelled(options),
		signal: abortController.signal
	};
}
/**
* Start the Gmail watcher service.
* Called automatically by the gateway if hooks.gmail is configured.
*/
async function startGmailWatcher(cfg, options = {}) {
	if (!cfg.hooks?.enabled) return {
		started: false,
		reason: "hooks not enabled"
	};
	if (!cfg.hooks?.gmail?.account) return {
		started: false,
		reason: "no gmail account configured"
	};
	if (!isGogAvailable()) return {
		started: false,
		reason: "gog binary not found"
	};
	const resolved = resolveGmailHookRuntimeConfig(cfg, {});
	if (!resolved.ok) return {
		started: false,
		reason: resolved.error
	};
	const runtimeConfig = resolved.value;
	if (isGmailWatcherStartCancelled(options)) return cancelledGmailWatcherStart(runtimeConfig);
	currentConfig = runtimeConfig;
	if (watcherProcess || renewInterval || respawnTimeout) {
		shuttingDown = true;
		if (respawnTimeout) {
			clearTimeout(respawnTimeout);
			respawnTimeout = null;
		}
		if (renewInterval) {
			clearInterval(renewInterval);
			renewInterval = null;
		}
		if (watcherProcess) {
			const oldProcess = watcherProcess;
			watcherProcess = null;
			await settleProcess(oldProcess);
			oldProcess.removeAllListeners();
		}
		shuttingDown = false;
	}
	if (runtimeConfig.tailscale.mode !== "off") {
		const cancellation = createGmailWatcherCancellation(options);
		try {
			await ensureTailscaleEndpoint({
				mode: runtimeConfig.tailscale.mode,
				path: runtimeConfig.tailscale.path,
				port: runtimeConfig.serve.port,
				signal: cancellation.signal,
				target: runtimeConfig.tailscale.target
			});
			log.info(`tailscale ${runtimeConfig.tailscale.mode} configured for port ${runtimeConfig.serve.port}`);
			if (cancellation.isCancelled()) return cancelledGmailWatcherStart(runtimeConfig);
		} catch (err) {
			if (cancellation.isCancelled()) return cancelledGmailWatcherStart(runtimeConfig);
			log.error(`tailscale setup failed: ${String(err)}`);
			return {
				started: false,
				reason: `tailscale setup failed: ${String(err)}`
			};
		} finally {
			cancellation.dispose();
		}
	}
	const cancellation = createGmailWatcherCancellation(options);
	const watchStarted = await startGmailWatch(runtimeConfig, { signal: cancellation.signal });
	cancellation.dispose();
	if (cancellation.isCancelled()) return cancelledGmailWatcherStart(runtimeConfig);
	if (!watchStarted) log.warn("gmail watch start failed, but continuing with serve");
	if (isGmailWatcherStartCancelled(options)) return cancelledGmailWatcherStart(runtimeConfig);
	shuttingDown = false;
	watcherProcess = spawnGogServe(runtimeConfig);
	const renewMs = runtimeConfig.renewEveryMinutes * 6e4;
	renewInterval = setInterval(() => {
		if (shuttingDown) return;
		startGmailWatch(runtimeConfig);
	}, renewMs);
	log.info(`gmail watcher started for ${runtimeConfig.account} (renew every ${runtimeConfig.renewEveryMinutes}m)`);
	return { started: true };
}
/**
* Stop the Gmail watcher service.
*/
async function stopGmailWatcher() {
	shuttingDown = true;
	if (respawnTimeout) {
		clearTimeout(respawnTimeout);
		respawnTimeout = null;
	}
	if (renewInterval) {
		clearInterval(renewInterval);
		renewInterval = null;
	}
	if (watcherProcess) {
		log.info("stopping gmail watcher");
		const proc = watcherProcess;
		watcherProcess = null;
		await settleProcess(proc);
	}
	currentConfig = null;
	log.info("gmail watcher stopped");
}
//#endregion
export { stopGmailWatcher as n, startGmailWatcher as t };
