import { acquireGatewayLock } from "../../infra/gateway-lock.js";
import { consumeGatewaySigusr1RestartAuthorization, isGatewaySigusr1RestartExternallyAllowed, } from "../../infra/restart.js";
import { createSubsystemLogger } from "../../logging/subsystem.js";
import { getActiveTaskCount, markShuttingDown, resetAllLanes, waitForActiveTasks, } from "../../process/command-queue.js";
import { createRestartIterationHook } from "../../process/restart-recovery.js";
const gatewayLog = createSubsystemLogger("gateway");
export async function runGatewayLoop(params) {
    const lock = await acquireGatewayLock();
    let server = null;
    let shuttingDown = false;
    let restartResolver = null;
    const cleanupSignals = () => {
        process.removeListener("SIGTERM", onSigterm);
        process.removeListener("SIGINT", onSigint);
        process.removeListener("SIGUSR1", onSigusr1);
    };
    const DRAIN_TIMEOUT_MS = 30_000;
    const SHUTDOWN_TIMEOUT_MS = 5_000;
    const request = (action, signal) => {
        if (shuttingDown) {
            gatewayLog.info(`received ${signal} during shutdown; ignoring`);
            return;
        }
        shuttingDown = true;
        // Prevent in-flight tasks from being resolved as COMPLETED so they
        // remain RUNNING and can be recovered on the next startup.
        markShuttingDown();
        const isRestart = action === "restart";
        gatewayLog.info(`received ${signal}; ${isRestart ? "restarting" : "shutting down"}`);
        // Allow extra time for draining active turns on restart.
        const forceExitMs = isRestart ? DRAIN_TIMEOUT_MS + SHUTDOWN_TIMEOUT_MS : SHUTDOWN_TIMEOUT_MS;
        const forceExitTimer = setTimeout(() => {
            gatewayLog.error("shutdown timed out; exiting without full cleanup");
            cleanupSignals();
            params.runtime.exit(0);
        }, forceExitMs);
        void (async () => {
            try {
                // On restart, wait for in-flight agent turns to finish before
                // tearing down the server so buffered messages are delivered.
                if (isRestart) {
                    const activeTasks = getActiveTaskCount();
                    if (activeTasks > 0) {
                        gatewayLog.info(`draining ${activeTasks} active task(s) before restart (timeout ${DRAIN_TIMEOUT_MS}ms)`);
                        const { drained } = await waitForActiveTasks(DRAIN_TIMEOUT_MS);
                        if (drained) {
                            gatewayLog.info("all active tasks drained");
                        }
                        else {
                            gatewayLog.warn("drain timeout reached; proceeding with restart");
                        }
                    }
                }
                await server?.close({
                    reason: isRestart ? "gateway restarting" : "gateway stopping",
                    restartExpectedMs: isRestart ? 1500 : null,
                });
            }
            catch (err) {
                gatewayLog.error(`shutdown error: ${String(err)}`);
            }
            finally {
                clearTimeout(forceExitTimer);
                server = null;
                if (isRestart) {
                    shuttingDown = false;
                    restartResolver?.();
                }
                else {
                    cleanupSignals();
                    params.runtime.exit(0);
                }
            }
        })();
    };
    const onSigterm = () => {
        gatewayLog.info("signal SIGTERM received");
        request("stop", "SIGTERM");
    };
    const onSigint = () => {
        gatewayLog.info("signal SIGINT received");
        request("stop", "SIGINT");
    };
    const onSigusr1 = () => {
        gatewayLog.info("signal SIGUSR1 received");
        const authorized = consumeGatewaySigusr1RestartAuthorization();
        if (!authorized && !isGatewaySigusr1RestartExternallyAllowed()) {
            gatewayLog.warn("SIGUSR1 restart ignored (not authorized; enable commands.restart or use gateway tool).");
            return;
        }
        request("restart", "SIGUSR1");
    };
    process.on("SIGTERM", onSigterm);
    process.on("SIGINT", onSigint);
    process.on("SIGUSR1", onSigusr1);
    try {
        // Reset logic (resetAllLanes) now lives unconditionally in the loop
        // body below so it also covers the first iteration, where node --watch
        // may have delivered a SIGTERM that set isShuttingDown before startup.
        const onIteration = createRestartIterationHook(() => { });
        // Keep process alive; SIGUSR1 triggers an in-process restart (no supervisor required).
        // SIGTERM/SIGINT still exit after a graceful shutdown.
        // eslint-disable-next-line no-constant-condition
        while (true) {
            onIteration();
            // Always reset shutdown flag and lane state at the start of each
            // iteration.  node --watch sends SIGTERM during restart which sets
            // isShuttingDown before the gateway has a chance to start.  On
            // first startup this is a safe no-op for the memory backend.
            resetAllLanes();
            server = await params.start();
            await new Promise((resolve) => {
                restartResolver = resolve;
            });
        }
    }
    finally {
        await lock?.release();
        cleanupSignals();
    }
}
