import pc from 'picocolors';
import { spawnSync } from 'node:child_process';
import { loadContext } from '../context.js';
import { printError } from '../output.js';
// Matches valid world IDs — same invariant enforced by enter.ts, destroy.ts, etc.
const SAFE_IDENT = /^[a-z0-9][a-z0-9-]{0,63}$/;
/**
 * Parse docker top stdout into ProcessRow[].
 * Accepts two formats:
 *   1. JSON: {"Titles":[...],"Processes":[[...],...]}  (HTTP API / docker daemon)
 *   2. Tabular text with a header line                 (bare CLI spawnSync path)
 *
 * The tabular format uses stime (single-word start time like "10:00" or "Feb11")
 * to avoid multi-word timestamp parsing issues.
 */
export function parseDockerTop(stdout) {
    const trimmed = stdout.trim();
    if (!trimmed)
        return [];
    let titles;
    let processes;
    if (trimmed.startsWith('{')) {
        try {
            const parsed = JSON.parse(trimmed);
            titles = (parsed.Titles ?? []).map((t) => t.toLowerCase().trim());
            processes = parsed.Processes ?? [];
        }
        catch {
            return [];
        }
    }
    else {
        const lines = trimmed.split('\n');
        if (lines.length < 2)
            return [];
        titles = (lines[0] ?? '').trim().toLowerCase().split(/\s+/);
        processes = lines.slice(1).map((l) => l.trim().split(/\s+/));
    }
    const idx = (candidates) => {
        for (const c of candidates) {
            const i = titles.indexOf(c);
            if (i !== -1)
                return i;
        }
        return -1;
    };
    const pidIdx = idx(['pid']);
    const userIdx = idx(['user', 'uid']);
    const cpuIdx = idx(['%cpu', 'cpu', 'pcpu']);
    const memIdx = idx(['%mem', 'mem', 'pmem']);
    const startIdx = idx(['stime', 'lstart', 'start_time', 'started', 'start']);
    const stateIdx = idx(['stat', 'state', 's']);
    const cmdIdx = idx(['cmd', 'command', 'args']);
    if (pidIdx === -1 || cmdIdx === -1)
        return [];
    return processes.map((parts) => {
        const cmdFull = parts.slice(cmdIdx).join(' ');
        return {
            pid: parts[pidIdx] ?? '',
            user: userIdx !== -1 ? (parts[userIdx] ?? '') : '',
            cpu: cpuIdx !== -1 ? (parts[cpuIdx] ?? '0.0') : '0.0',
            mem: memIdx !== -1 ? (parts[memIdx] ?? '0.0') : '0.0',
            started: startIdx !== -1 ? (parts[startIdx] ?? '') : '',
            state: stateIdx !== -1 ? (parts[stateIdx] ?? '') : '',
            command: cmdFull,
        };
    }).filter((r) => r.pid !== '');
}
export function sortRows(rows, key) {
    return [...rows].sort((a, b) => {
        if (key === 'pid')
            return parseInt(b.pid, 10) - parseInt(a.pid, 10);
        return parseFloat(b[key]) - parseFloat(a[key]);
    });
}
function printTable(rows) {
    const cols = process.stdout.columns ?? 80;
    const fixedWidth = 5 + 1 + 8 + 1 + 6 + 1 + 6 + 1 + 10 + 1 + 6 + 1;
    const cmdWidth = Math.max(20, cols - fixedWidth);
    const header = [
        pc.bold(pc.dim('PID'.padEnd(5))),
        pc.bold(pc.dim('USER'.padEnd(8))),
        pc.bold(pc.dim('%CPU'.padEnd(6))),
        pc.bold(pc.dim('%MEM'.padEnd(6))),
        pc.bold(pc.dim('STARTED'.padEnd(10))),
        pc.bold(pc.dim('STATE'.padEnd(6))),
        pc.bold(pc.dim('COMMAND')),
    ].join(' ');
    console.log(header);
    for (const row of rows) {
        const cmd = row.command.length > cmdWidth
            ? row.command.slice(0, cmdWidth - 1) + '…'
            : row.command;
        const stateFn = row.state.startsWith('R')
            ? pc.green
            : row.state.startsWith('Z')
                ? pc.red
                : pc.dim;
        console.log([
            row.pid.padEnd(5),
            row.user.slice(0, 8).padEnd(8),
            row.cpu.slice(0, 5).padEnd(6),
            row.mem.slice(0, 5).padEnd(6),
            row.started.slice(0, 10).padEnd(10),
            stateFn(row.state.slice(0, 6).padEnd(6)),
            cmd,
        ].join(' '));
    }
}
export function registerPs(program) {
    program
        .command('ps')
        .description('List running processes in a world container')
        .argument('<world>', 'World ID')
        .option('--watch', 'Refresh every 5 seconds (Ctrl-C to exit)')
        .option('--sort <key>', 'Sort column: cpu, mem, pid', 'cpu')
        .action(async (worldId, opts) => {
        if (!SAFE_IDENT.test(worldId)) {
            printError(`Invalid world ID "${worldId}". World IDs must match [a-z0-9][a-z0-9-]{0,63}.`);
            process.exitCode = 1;
            return;
        }
        const { ctx, error } = await loadContext();
        if (!ctx) {
            printError(error?.message ?? 'Olam is not configured. Run `olam init` first.');
            process.exitCode = 1;
            return;
        }
        const sortKey = ['cpu', 'mem', 'pid'].includes(opts.sort)
            ? opts.sort
            : 'cpu';
        const containerName = `olam-${worldId}-devbox`;
        let watchInterval;
        function fetchAndPrint() {
            const result = spawnSync('docker', ['top', containerName, 'pid', 'user', 'pcpu', 'pmem', 'stime', 'stat', 'cmd'], { encoding: 'utf-8', timeout: 3000 });
            if (result.status !== 0 || result.error) {
                const msg = result.stderr?.trim() || result.error?.message || 'docker top failed';
                printError(`ps failed for "${worldId}": ${msg}`);
                if (watchInterval !== undefined) {
                    clearInterval(watchInterval);
                    process.exit(1);
                }
                process.exitCode = 1;
                return;
            }
            const rows = sortRows(parseDockerTop(result.stdout ?? ''), sortKey);
            if (opts.watch)
                process.stdout.write('\x1b[2J\x1b[H');
            printTable(rows);
            if (opts.watch) {
                process.stdout.write(`\n${pc.dim(`world: ${worldId}  sort: ${sortKey}  refresh: 5s  Ctrl-C to exit`)}\n`);
            }
        }
        fetchAndPrint();
        if (opts.watch) {
            watchInterval = setInterval(fetchAndPrint, 5000);
            process.on('SIGINT', () => {
                clearInterval(watchInterval);
                process.exit(0);
            });
        }
    });
}
//# sourceMappingURL=ps.js.map