"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.registerStopCommand = registerStopCommand;
const fs_1 = __importDefault(require("fs"));
const os_1 = __importDefault(require("os"));
const path_1 = __importDefault(require("path"));
const daemon_discovery_js_1 = require("../../lib/daemon-discovery.js");
const port_utils_js_1 = require("../port-utils.js");
const ui_js_1 = require("../ui.js");
/**
 * Two-stage shutdown per managed process: SIGTERM, wait up to 1.5s,
 * escalate to SIGKILL if still alive. Don't unlink the pidfile until
 * the process is confirmed dead — otherwise an orphan that ignores
 * SIGTERM keeps running while we forget about it (the bug behind the
 * "stale next-server serving 500s" incident on 2026-05-03).
 *
 * v0.8: PID lookup goes through `~/.chorus/daemon.json` first, falling
 * back to the legacy daemon.pid / web.pid files for compat with
 * v0.7-and-earlier installs. Belt-and-braces port sweep targets the
 * recorded ports (or defaults if no daemon.json).
 */
function registerStopCommand(program) {
    program
        .command('stop')
        .description('Stop the Chorus daemon and cockpit')
        .action(async () => {
        try {
            const chorusDir = path_1.default.join(os_1.default.homedir(), '.chorus');
            const daemonPidFile = path_1.default.join(chorusDir, 'daemon.pid');
            const webPidFile = path_1.default.join(chorusDir, 'web.pid');
            const info = (0, daemon_discovery_js_1.readDaemonInfo)();
            const daemonPort = info?.daemonPort ?? daemon_discovery_js_1.DEFAULT_DAEMON_PORT;
            const cockpitPort = info?.cockpitPort ?? daemon_discovery_js_1.DEFAULT_COCKPIT_PORT;
            const daemonPidfileExists = fs_1.default.existsSync(daemonPidFile);
            const webPidfileExists = fs_1.default.existsSync(webPidFile);
            const daemonPortInUse = await (0, port_utils_js_1.isPortInUse)(daemonPort);
            const cockpitPortInUse = await (0, port_utils_js_1.isPortInUse)(cockpitPort);
            if (!info &&
                !daemonPidfileExists &&
                !webPidfileExists &&
                !daemonPortInUse &&
                !cockpitPortInUse) {
                console.log('');
                console.log((0, ui_js_1.header)(ui_js_1.sym.info, 'Chorus is not running', 'nothing to stop'));
                console.log('');
                return;
            }
            console.log('');
            console.log((0, ui_js_1.header)(ui_js_1.sym.pointer, 'Stopping Chorus...'));
            console.log('');
            // Prefer daemon.json (v0.8), fall back to pidfiles (v0.7).
            if (info) {
                await stopByPid('Daemon', info.daemonPid);
                if (info.cockpitPid) {
                    await stopByPid('Cockpit', info.cockpitPid);
                }
            }
            await stopByPidFile('Daemon', daemonPidFile);
            await stopByPidFile('Cockpit', webPidFile);
            // Port-based sweep — kills any chorus-owned listener that
            // escaped the pidfile path.
            await sweepPort(daemonPort, 'Daemon');
            await sweepPort(cockpitPort, 'Cockpit');
            (0, daemon_discovery_js_1.clearDaemonInfo)();
            console.log('');
        }
        catch (error) {
            console.error(`${ui_js_1.sym.err} ${ui_js_1.c.red('Error stopping chorus:')}`, error);
            process.exit(1);
        }
    });
}
async function stopByPid(label, pid) {
    if (!Number.isFinite(pid) || pid <= 0)
        return;
    const dead = await (0, port_utils_js_1.killAndVerify)(pid, label);
    if (dead) {
        console.log(`  ${ui_js_1.sym.ok} ${label.padEnd(7)} ${ui_js_1.c.dim(`(PID ${pid})`)}`);
    }
}
async function stopByPidFile(label, pidFile) {
    if (!fs_1.default.existsSync(pidFile))
        return;
    const pid = parseInt(fs_1.default.readFileSync(pidFile, 'utf-8'), 10);
    if (!Number.isFinite(pid) || pid <= 0) {
        fs_1.default.unlinkSync(pidFile);
        return;
    }
    const dead = await (0, port_utils_js_1.killAndVerify)(pid, label);
    if (dead) {
        console.log(`  ${ui_js_1.sym.ok} ${label.padEnd(7)} ${ui_js_1.c.dim(`(PID ${pid})`)}`);
        try {
            fs_1.default.unlinkSync(pidFile);
        }
        catch {
            /* already gone */
        }
    }
}
async function sweepPort(port, label) {
    const pids = (0, port_utils_js_1.findPidsOnPort)(port);
    for (const pid of pids) {
        const dead = await (0, port_utils_js_1.killAndVerify)(pid, `${label} orphan`);
        if (dead) {
            console.log(`  ${ui_js_1.sym.ok} ${label.padEnd(7)} ${ui_js_1.c.dim(`(orphan PID ${pid} on :${port})`)}`);
        }
    }
}
//# sourceMappingURL=stop.js.map