import { spawn, execFileSync, execSync } from 'node:child_process';
import fs from 'node:fs';
import path from 'node:path';
import chalk from 'chalk';
import { BLOCKRUN_DIR, DEFAULT_PROXY_PORT } from '../config.js';
const PID_FILE = path.join(BLOCKRUN_DIR, 'franklin.pid');
const LOG_FILE = path.join(BLOCKRUN_DIR, 'franklin-debug.log');
function readPid() {
    try {
        const raw = fs.readFileSync(PID_FILE, 'utf-8').trim();
        const pid = parseInt(raw, 10);
        return isNaN(pid) ? null : pid;
    }
    catch {
        return null;
    }
}
function isRunning(pid) {
    try {
        process.kill(pid, 0);
    }
    catch {
        return false;
    }
    // PID may have been recycled. Try to confirm command line looks like Franklin.
    // Platform fallbacks (some env lack `ps`): Linux /proc → ps → give up.
    try {
        // Linux (including Alpine/busybox containers) exposes /proc/<pid>/cmdline
        const procCmdline = `/proc/${pid}/cmdline`;
        if (fs.existsSync(procCmdline)) {
            const raw = fs.readFileSync(procCmdline, 'utf-8').replace(/\0/g, ' ').trim();
            return /franklin|runcode|node.*dist\/index/.test(raw);
        }
    }
    catch { /* fall through to ps */ }
    try {
        const cmd = execSync(`ps -p ${pid} -o command=`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }).trim();
        return /franklin|runcode|node.*dist\/index/.test(cmd);
    }
    catch {
        // No ps, no /proc — give up and trust the kill-0 check
        return true;
    }
}
function findDaemonBinary() {
    for (const name of ['franklin', 'runcode']) {
        try {
            return execFileSync('which', [name], { encoding: 'utf-8' }).trim();
        }
        catch {
            // Try the legacy alias next.
        }
    }
    return null;
}
export async function daemonCommand(action, options) {
    const port = parseInt(options.port || String(DEFAULT_PROXY_PORT));
    if (isNaN(port) || port < 1 || port > 65535) {
        console.log(chalk.red(`Invalid port "${options.port}". Must be 1-65535. Default: ${DEFAULT_PROXY_PORT}`));
        return;
    }
    switch (action) {
        case 'start': {
            const existing = readPid();
            if (existing && isRunning(existing)) {
                console.log(chalk.yellow(`franklin daemon already running (PID ${existing})`));
                console.log(chalk.dim(`  Proxy: http://localhost:${port}/api`));
                return;
            }
            const daemonBin = findDaemonBinary();
            if (!daemonBin) {
                console.log(chalk.red('franklin binary not found in PATH.'));
                return;
            }
            fs.mkdirSync(BLOCKRUN_DIR, { recursive: true });
            const child = spawn(daemonBin, ['proxy', '--port', String(port)], {
                detached: true,
                // stdout → /dev/null (banner + startup messages), stderr → log file (debug/errors only)
                stdio: ['ignore', 'ignore', fs.openSync(LOG_FILE, 'a')],
            });
            child.unref();
            fs.writeFileSync(PID_FILE, String(child.pid));
            console.log(chalk.green(`✓ franklin daemon started (PID ${child.pid})`));
            console.log(chalk.dim(`  Proxy: http://localhost:${port}/api`));
            console.log(chalk.dim(`  Logs:  ${LOG_FILE}`));
            break;
        }
        case 'stop': {
            const pid = readPid();
            if (!pid) {
                console.log(chalk.yellow('No franklin daemon found.'));
                return;
            }
            if (!isRunning(pid)) {
                fs.unlinkSync(PID_FILE);
                console.log(chalk.yellow(`Daemon PID ${pid} not running — cleaned up.`));
                return;
            }
            try {
                process.kill(pid, 'SIGTERM');
                // Wait for process to exit (up to 5s)
                for (let i = 0; i < 50; i++) {
                    if (!isRunning(pid))
                        break;
                    await new Promise(r => setTimeout(r, 100));
                }
                if (isRunning(pid)) {
                    process.kill(pid, 'SIGKILL');
                }
                try {
                    fs.unlinkSync(PID_FILE);
                }
                catch { /* already gone */ }
                console.log(chalk.green(`✓ franklin daemon stopped (PID ${pid})`));
            }
            catch (e) {
                console.log(chalk.red(`Failed to stop daemon: ${e.message}`));
            }
            break;
        }
        case 'status': {
            const pid = readPid();
            if (!pid) {
                console.log(chalk.dim('franklin daemon: not running'));
                return;
            }
            if (isRunning(pid)) {
                console.log(chalk.green(`✓ franklin daemon running`));
                console.log(`  PID:   ${chalk.bold(pid)}`);
                console.log(`  Proxy: ${chalk.cyan(`http://localhost:${port}/api`)}`);
                console.log(chalk.dim(`  Logs:  ${LOG_FILE}`));
            }
            else {
                fs.unlinkSync(PID_FILE);
                console.log(chalk.yellow('franklin daemon: not running (stale PID cleaned up)'));
            }
            break;
        }
        default:
            console.log(chalk.red(`Unknown daemon action: ${action}`));
            console.log('Usage: franklin daemon <start|stop|status>');
    }
}
