import { spawn as cpSpawn } from 'node:child_process';
import { getCliDefinition } from '../cli-registry.js';
import { resolveCliSync } from '../cli-resolver.js';
import { runVerification } from './verification.js';
function resolveNonInteractiveCli(cli) {
    if (cli !== 'cursor') {
        return cli;
    }
    const resolved = resolveCliSync('cursor');
    return resolved?.binary ?? 'agent';
}
export function buildCommand(cli, extraArgs = [], task) {
    if (cli === 'api') {
        throw new Error('cli "api" uses direct API calls, not a subprocess command');
    }
    const resolvedCli = resolveNonInteractiveCli(cli);
    const definition = getCliDefinition(resolvedCli);
    if (!definition || definition.binaries.length === 0) {
        throw new Error(`Unknown or non-executable CLI: ${resolvedCli}`);
    }
    return [definition.binaries[0], ...definition.nonInteractiveArgs(task, extraArgs)];
}
export function spawnProcess(command, options) {
    const [bin, ...args] = command;
    return cpSpawn(bin, args, options);
}
export function collectOutput(process) {
    return new Promise((resolve, reject) => {
        let settled = false;
        const stdout = [];
        const stderr = [];
        process.stdout?.on('data', (chunk) => {
            stdout.push(chunk.toString());
        });
        process.stderr?.on('data', (chunk) => {
            stderr.push(chunk.toString());
        });
        process.once('error', (err) => {
            if (!settled) {
                settled = true;
                reject(err);
            }
        });
        process.once('close', () => {
            if (!settled) {
                settled = true;
                resolve(`${stdout.join('')}${stderr.join('')}`);
            }
        });
    });
}
export function detectCompletion(output, verification) {
    if (/OWNER_DECISION:\s*(?:INCOMPLETE_RETRY|INCOMPLETE_FAIL|NEEDS_CLARIFICATION)\b/i.test(output)) {
        return false;
    }
    if (/OWNER_DECISION:\s*COMPLETE\b/i.test(output)) {
        return true;
    }
    if (/\bSTEP_COMPLETE:([A-Za-z0-9_.:-]+)/.test(output)) {
        return true;
    }
    if (!verification) {
        return false;
    }
    return runVerification(verification, output, 'process', undefined, { allowFailure: true }).passed;
}
async function runCommand(command, opts) {
    const child = spawnProcess([command.bin, ...command.args], {
        cwd: opts.cwd,
        env: { ...process.env, ...command.env },
        stdio: 'pipe',
    });
    const outputPromise = collectOutput(child);
    const exitPromise = new Promise((resolve, reject) => {
        let timedOut = false;
        let timer;
        let killTimer;
        if (opts.timeoutMs) {
            timer = setTimeout(() => {
                timedOut = true;
                child.kill('SIGTERM');
                killTimer = setTimeout(() => child.kill('SIGKILL'), 5000);
            }, opts.timeoutMs);
        }
        const clearTimer = () => {
            if (timer)
                clearTimeout(timer);
            if (killTimer)
                clearTimeout(killTimer);
        };
        child.once('error', (error) => {
            clearTimer();
            reject(error);
        });
        child.once('close', (exitCode, exitSignal) => {
            clearTimer();
            if (timedOut) {
                reject(new Error(`Process timed out after ${opts.timeoutMs ?? 'unknown'}ms`));
                return;
            }
            resolve({
                exitCode: exitCode ?? undefined,
                exitSignal: exitSignal ?? undefined,
            });
        });
    });
    const [outputResult, exitResult] = await Promise.allSettled([outputPromise, exitPromise]);
    const output = outputResult.status === 'fulfilled' ? outputResult.value : '';
    if (exitResult.status === 'rejected') {
        const err = exitResult.reason instanceof Error ? exitResult.reason : new Error(String(exitResult.reason));
        err.partialOutput = output;
        throw err;
    }
    return {
        output,
        exitCode: exitResult.value.exitCode,
        exitSignal: exitResult.value.exitSignal,
    };
}
export function createProcessSpawner(deps) {
    const buildAgentCommand = (agent, task) => {
        const extraArgs = agent.constraints?.model ? ['--model', agent.constraints.model] : [];
        const [bin, ...args] = buildCommand(agent.cli, extraArgs, task);
        return { bin, args };
    };
    return {
        async spawnShell(command, opts) {
            return runCommand({ bin: 'sh', args: ['-c', command] }, { ...opts, cwd: opts.cwd ?? deps.cwd });
        },
        async spawnAgent(agent, task, opts) {
            return runCommand(buildAgentCommand(agent, task), { ...opts, cwd: opts.cwd ?? deps.cwd });
        },
        async spawnInteractive(agent, task, opts) {
            return runCommand(buildAgentCommand(agent, task), { ...opts, cwd: opts.cwd ?? deps.cwd });
        },
        buildCommand(agent, task) {
            return buildAgentCommand(agent, task);
        },
    };
}
//# sourceMappingURL=process-spawner.js.map