// ── 主函数 ──────────────────────────────────────────────────
/**
 * setupShutdown — 注册优雅关闭信号处理器
 *
 * 在 bootstrap 步骤 ⑧ 中调用。
 *
 * 注册流程：
 *   1. 创建 handleSignal 闭包（调用 internals.shutdown(serverHandle)）
 *   2. 在 process 上注册 SIGTERM / SIGINT 信号监听
 *   3. 返回 cleanup 函数，调用后移除监听器
 *
 * 信号处理行为：
 *   - SIGTERM：Docker / Kubernetes / PM2 发送，表示要求进程优雅退出
 *   - SIGINT：用户按 Ctrl+C 发送
 *   - 两个信号触发相同的关闭流程
 *   - internals.shutdown 内部有 _shuttingDown guard，防止重复触发
 *
 * @param options 关闭配置
 * @returns cleanup 函数，用于移除信号处理器
 */
export function setupShutdown(options) {
    const { internals, serverHandle, logger, testMode = false } = options;
    // 测试模式下不注册信号处理器
    // createTestApp 会直接调用 internals.shutdown() 控制生命周期
    if (testMode) {
        return () => {
            // no-op：测试模式下无需清理
        };
    }
    // ── 创建信号处理函数 ──────────────────────────────────────
    //
    // 使用箭头函数创建闭包，持有 internals / serverHandle / logger 引用。
    // internals.shutdown() 内部有 _shuttingDown guard，
    // 即使 SIGTERM 和 SIGINT 同时或连续触发，也只会执行一次关闭流程。
    //
    const handleSignal = (signal) => {
        logger.info(`[vextjs] received ${signal}, starting graceful shutdown...`);
        // internals.shutdown() 是异步的，但信号处理器不能是 async
        // 使用 .catch() 捕获错误，避免 unhandled promise rejection
        internals.shutdown(serverHandle).catch((err) => {
            logger.error({ error: err.message }, `[vextjs] shutdown error after ${signal}`);
            // shutdown 失败是严重错误，强制退出
            // 使用 exit code 1 表示异常退出
            process.exit(1);
        });
    };
    // ── 注册信号监听 ──────────────────────────────────────────
    //
    // 使用 process.on（而非 process.once）：
    //   - process.once 在收到第一个信号后自动移除监听器
    //   - 如果 shutdown 流程耗时较长，用户可能再次按 Ctrl+C
    //   - 第二次信号到达时 _shuttingDown guard 会直接返回（不重复执行）
    //
    // 使用命名的 wrapper 函数以便 cleanup 时精确移除：
    //
    const onSigterm = () => handleSignal("SIGTERM");
    const onSigint = () => handleSignal("SIGINT");
    process.on("SIGTERM", onSigterm);
    process.on("SIGINT", onSigint);
    // ── 注册 IPC shutdown 消息监听（Windows 兼容）─────────────
    //
    // 🐛 修复 BUG-014：Windows 上 child.kill('SIGTERM') 不会触发子进程的
    // process.on('SIGTERM') 处理器——Node.js 在 Windows 上直接调用
    // TerminateProcess API 杀死进程，导致 onClose hooks 不执行。
    //
    // 解决方案：当进程有 IPC 通道时（process.send 存在，表明是 fork 子进程），
    // 额外注册 process.on('message') 监听 { type: 'shutdown' } 消息。
    // CLI（start.ts）和 ColdRestarter 在 Windows 上发送此 IPC 消息替代 SIGTERM。
    //
    // Unix 上此监听器同样生效（双重保障），不影响现有 SIGTERM 流程。
    // internals.shutdown 内部有 _shuttingDown guard，不会重复执行。
    //
    let onMessage = null;
    if (process.send) {
        onMessage = (msg) => {
            if (typeof msg === "object" &&
                msg !== null &&
                msg.type === "shutdown") {
                handleSignal("IPC:shutdown");
            }
        };
        process.on("message", onMessage);
    }
    // ── 返回清理函数 ──────────────────────────────────────────
    //
    // cleanup 移除已注册的信号处理器，防止：
    //   - 多实例场景下旧实例的处理器持续存在
    //   - 测试场景下信号处理器泄漏
    //
    return () => {
        process.removeListener("SIGTERM", onSigterm);
        process.removeListener("SIGINT", onSigint);
        if (onMessage) {
            process.removeListener("message", onMessage);
        }
    };
}
/**
 * createShutdownHandler — 创建独立的关闭处理函数
 *
 * 用于需要手动触发关闭的场景（而非信号触发），例如：
 *   - Cluster Master 通知 Worker 关闭
 *   - 健康检查失败后主动关闭
 *   - 集成测试中的手动关闭
 *
 * 与 setupShutdown 的区别：
 *   - setupShutdown 注册信号处理器（自动触发）
 *   - createShutdownHandler 返回手动调用的函数
 *
 * @param internals 框架内部方法
 * @param serverHandle HTTP 服务器句柄
 * @returns 异步关闭函数
 *
 * @example
 * ```typescript
 * const shutdown = createShutdownHandler(internals, serverHandle)
 *
 * // 某个条件触发时手动关闭
 * if (shouldShutdown) {
 *   await shutdown()
 * }
 * ```
 */
export function createShutdownHandler(internals, serverHandle) {
    return async () => {
        await internals.shutdown(serverHandle);
    };
}
