/**
 * olam pr — inspect and decide PR-gate requests opened by in-world hooks.
 *
 * Each running world's PreToolUse hook intercepts `gh pr create` and
 * POSTs a gate to its own control plane. This CLI surface lets the
 * human fan out across all running worlds, pick a gate, and approve
 * or block it. Codex adversarial review (future) will plug in beside
 * this as a second decision source.
 *
 *   olam pr list                                list all pending gates
 *   olam pr show <id>                           full detail (diff, cmd)
 *   olam pr approve <id> [--reason <r>]         let the PR proceed
 *   olam pr reject <id> [--reason <r>]          block with a reason
 */
import pc from 'picocolors';
import { WorldRegistry } from '@olam/core/src/world/registry.js';
import { decideGate, getGate, listGates, } from '@olam/core/src/pr-gate/index.js';
import { printError, printHeader, printInfo, printSuccess } from '../output.js';
function runningWorlds() {
    const registry = new WorldRegistry();
    try {
        return registry.list({ status: 'running' });
    }
    finally {
        registry.close();
    }
}
async function fanoutList() {
    const worlds = runningWorlds();
    const results = [];
    await Promise.all(worlds.map(async (world) => {
        try {
            const gates = await listGates(world.portOffset);
            for (const gate of gates)
                results.push({ world, gate });
        }
        catch {
            // World control plane unreachable — skip, don't fail the whole list
        }
    }));
    results.sort((a, b) => b.gate.openedAt.localeCompare(a.gate.openedAt));
    return results;
}
async function findGate(id) {
    const worlds = runningWorlds();
    for (const world of worlds) {
        try {
            const gate = await getGate(world.portOffset, id);
            if (gate)
                return { world, gate };
        }
        catch {
            // Skip unreachable worlds
        }
    }
    return null;
}
function formatStateBadge(state) {
    if (state === 'approve')
        return pc.green('approve');
    if (state === 'block')
        return pc.red('block');
    // Phase C C2 (CP3 ADV-C2-HIGH3 fix): explicit branch for the new
    // 'denied' state so `olam pr list` shows the C2-era reaper-takes-over
    // gates as gray 'denied' instead of falling through to the default
    // yellow 'pending' badge — which would have misled operators into
    // running `olam pr approve <id>` and getting a 409 because
    // handlePrGateDecide rejects non-pending gates.
    if (state === 'denied')
        return pc.gray('denied');
    return pc.yellow('pending');
}
export function registerPr(program) {
    const pr = program.command('pr').description('Review and decide PR-gate requests from running worlds');
    pr.command('list')
        .description('List all PR-gate requests across every running world')
        .action(async () => {
        const all = await fanoutList();
        if (all.length === 0) {
            console.log(pc.dim('No gates open.'));
            return;
        }
        printHeader(`${all.length} gate(s)`);
        for (const { world, gate } of all) {
            const badge = formatStateBadge(gate.state);
            console.log(`  ${pc.bold(gate.id.slice(0, 8))} ${badge.padEnd(20)} ${pc.dim(world.name)} ${pc.dim(gate.branch)}`);
            console.log(`    ${pc.dim(gate.command.slice(0, 100))}`);
        }
    });
    pr.command('show')
        .description('Show full gate detail (diff, command, commits)')
        .argument('<id>', 'Gate id (prefix match ok)')
        .action(async (id) => {
        const found = await findGate(id);
        if (!found) {
            printError(`No gate found with id "${id}" in any running world.`);
            process.exitCode = 1;
            return;
        }
        const { world, gate } = found;
        printHeader(`Gate ${gate.id}`);
        printInfo('World', `${world.name} (${world.id})`);
        printInfo('State', gate.state);
        printInfo('Branch', gate.branch);
        printInfo('Base', gate.base);
        printInfo('Command', gate.command);
        printInfo('Opened', gate.openedAt);
        if (gate.decidedAt) {
            printInfo('Decided', `${gate.decidedAt} by ${gate.decidedBy ?? 'unknown'}`);
            if (gate.reason)
                printInfo('Reason', gate.reason);
        }
        printHeader('Commits');
        console.log(gate.commitLog || pc.dim('  (empty)'));
        printHeader('Diff stat');
        console.log(gate.diffStat || pc.dim('  (empty)'));
    });
    function decideCommand(verb, decision) {
        pr.command(verb)
            .description(`${verb[0].toUpperCase()}${verb.slice(1)} a pending gate`)
            .argument('<id>', 'Gate id')
            .option('--reason <reason>', 'Short free-text reason')
            .option('--by <name>', 'Deciding identity (defaults to $USER)', process.env['USER'] ?? 'human')
            .action(async (id, opts) => {
            const found = await findGate(id);
            if (!found) {
                printError(`No gate found with id "${id}" in any running world.`);
                process.exitCode = 1;
                return;
            }
            try {
                const result = await decideGate(found.world.portOffset, found.gate.id, {
                    decision,
                    by: opts.by,
                    ...(opts.reason ? { reason: opts.reason } : {}),
                });
                printSuccess(`Gate ${result.id}: ${result.state}`);
            }
            catch (err) {
                printError(err instanceof Error ? err.message : String(err));
                process.exitCode = 1;
            }
        });
    }
    decideCommand('approve', 'approve');
    decideCommand('reject', 'block');
}
//# sourceMappingURL=pr.js.map