import { removePidFile } from "../utils/pid.js";
import { closeDatabase } from "../db/database.js";
import { logger } from "../utils/logger.js";
const SHUTDOWN_TIMEOUT_MS = 5_000;
const INFLIGHT_DRAIN_TIMEOUT_MS = 3_000;
const shutdownHandlers = [];
let isShuttingDown = false;
let inflightCount = 0;
let inflightResolve = null;
/**
 * Register a handler to be called during graceful shutdown.
 * Handlers are called in reverse registration order (LIFO).
 */
export function registerShutdownHandler(name, handler) {
    shutdownHandlers.push({ name, handler });
}
/**
 * Clear all registered shutdown handlers (for testing).
 */
export function clearShutdownHandlers() {
    shutdownHandlers.length = 0;
    isShuttingDown = false;
    inflightCount = 0;
    inflightResolve = null;
}
/**
 * Check if the daemon is currently shutting down.
 */
export function isShutdownInProgress() {
    return isShuttingDown;
}
/**
 * Track an in-flight request. Call the returned function when the request completes.
 * During shutdown, the orchestrator waits for all in-flight requests to drain.
 */
export function trackInflight() {
    inflightCount++;
    let released = false;
    return () => {
        if (released)
            return;
        released = true;
        inflightCount--;
        if (inflightCount <= 0 && inflightResolve) {
            inflightResolve();
            inflightResolve = null;
        }
    };
}
/**
 * Get the number of in-flight requests (for testing/monitoring).
 */
export function getInflightCount() {
    return inflightCount;
}
/**
 * Wait for all in-flight requests to complete, with a timeout.
 */
function waitForInflight(timeoutMs) {
    if (inflightCount <= 0) {
        return Promise.resolve();
    }
    return new Promise((resolve) => {
        const timeout = setTimeout(() => {
            logger.warn(`In-flight drain timed out with ${inflightCount} request(s) remaining`);
            inflightResolve = null;
            resolve();
        }, timeoutMs);
        inflightResolve = () => {
            clearTimeout(timeout);
            resolve();
        };
    });
}
/**
 * Execute the graceful shutdown sequence:
 * 1. Stop accepting new requests (mark as shutting down)
 * 2. Wait for in-flight requests to complete (with timeout)
 * 3. Run registered shutdown handlers (reverse order)
 * 4. Close SQLite connection
 * 5. Remove PID file
 *
 * The entire sequence is wrapped in an overall timeout. If shutdown
 * takes longer than SHUTDOWN_TIMEOUT_MS, remaining steps are skipped
 * and shutdown completes with a warning.
 */
export async function gracefulShutdown() {
    if (isShuttingDown) {
        logger.info("Shutdown already in progress");
        return;
    }
    isShuttingDown = true;
    logger.info("Starting graceful shutdown...");
    // Race entire shutdown against overall timeout
    const shutdownPromise = executeShutdownSequence();
    const timeoutPromise = new Promise((resolve) => {
        setTimeout(() => resolve("timeout"), SHUTDOWN_TIMEOUT_MS);
    });
    const result = await Promise.race([shutdownPromise.then(() => "done"), timeoutPromise]);
    if (result === "timeout") {
        logger.warn("Shutdown timed out, forcing completion");
        // Still attempt to clean up PID file and database even on timeout
        await cleanupFinal();
    }
    logger.info("Shutdown complete");
}
async function executeShutdownSequence() {
    // Wait for in-flight requests to drain
    if (inflightCount > 0) {
        logger.info(`Waiting for ${inflightCount} in-flight request(s) to complete...`);
        await waitForInflight(INFLIGHT_DRAIN_TIMEOUT_MS);
    }
    // Run handlers in reverse order (LIFO)
    for (let i = shutdownHandlers.length - 1; i >= 0; i--) {
        const { name, handler } = shutdownHandlers[i];
        try {
            logger.info(`Shutdown: ${name}`);
            await handler();
        }
        catch (err) {
            logger.error(`Shutdown handler '${name}' failed: ${String(err)}`);
        }
    }
    await cleanupFinal();
}
async function cleanupFinal() {
    // Close database
    try {
        logger.info("Shutdown: closing database");
        closeDatabase();
    }
    catch (err) {
        logger.error(`Failed to close database: ${String(err)}`);
    }
    // Remove PID file
    try {
        logger.info("Shutdown: removing PID file");
        await removePidFile();
    }
    catch (err) {
        logger.error(`Failed to remove PID file: ${String(err)}`);
    }
}
//# sourceMappingURL=shutdown.js.map