/**
 * ============================================================
 *  src/plugins/verificationGate.ts —— ralph-loop 的"完成验证门"
 * ------------------------------------------------------------
 *  Agent 输出 <promise>DONE</promise> 哨兵后，pluginRunner 跑这里的
 *  checks。全部通过才真正退出循环；任一未通过则回 BUILD 继续干。
 *
 *  支持四种 check：
 *    - shell:<cmd>                跨平台 spawn（Windows 走 cmd /c, 其余 bash -c）
 *    - file_exists:<path>         fs.existsSync
 *    - file_contains:<file>:<re>  正则匹配文件内容
 *    - test_count:<min>           跑 `bun test`，从 stdout 解析 "N pass"
 *
 *  工作目录隔离：所有相对路径以 getWorkingDir() 为锚，子进程 cwd 也用它，
 *  确保并行 session 不会互相串台。
 * ============================================================
 */
import { existsSync, readFileSync } from 'node:fs';
import { spawn } from 'node:child_process';
function parseVerifyArg(arg) {
    const colonIdx = arg.indexOf(':');
    if (colonIdx < 0)
        return null;
    const type = arg.slice(0, colonIdx).trim().toLowerCase();
    const value = arg.slice(colonIdx + 1).trim();
    switch (type) {
        case 'shell':
            return { type: 'shell', command: value, timeout: 30_000 };
        case 'file_exists':
            return { type: 'file_exists', file: value };
        case 'file_contains': {
            const sep = value.indexOf(':');
            if (sep < 0)
                return null;
            return {
                type: 'file_contains',
                file: value.slice(0, sep),
                pattern: value.slice(sep + 1),
            };
        }
        case 'test_count': {
            const count = parseInt(value, 10);
            if (isNaN(count) || count < 0)
                return null;
            return { type: 'test_count', minCount: count };
        }
        default:
            return null;
    }
}
export function parseVerifyArgs(args) {
    const checks = [];
    const regex = /--verify\s+("[^"]*"|\S+)/gi;
    let match;
    while ((match = regex.exec(args)) !== null) {
        const raw = match[1].replace(/^"|"$/g, '');
        const check = parseVerifyArg(raw);
        if (check)
            checks.push(check);
    }
    return checks;
}
function runShell(command, timeout) {
    return new Promise((resolve) => {
        const isWin = process.platform === 'win32';
        // 不显式传 cwd —— 继承 process.cwd()，main.tsx 已 chdir 到 workingDir，
        // 而测试里直接用 process.cwd()，避免依赖 mock 的 getWorkingDir 造成串台
        const child = isWin
            ? spawn('cmd', ['/c', command], { timeout, env: process.env })
            : spawn('bash', ['-c', command], { timeout, env: process.env });
        let stdout = '';
        let stderr = '';
        child.stdout.on('data', (d) => {
            stdout += d.toString();
        });
        child.stderr.on('data', (d) => {
            stderr += d.toString();
        });
        child.on('error', () => {
            resolve({ exitCode: null, stdout, stderr, errored: true });
        });
        child.on('close', (code) => {
            resolve({ exitCode: code, stdout, stderr, errored: false });
        });
    });
}
export async function verifyShell(command, timeout) {
    const r = await runShell(command, timeout);
    if (r.errored) {
        return {
            check: { type: 'shell', command },
            passed: false,
            output: `执行失败`,
        };
    }
    return {
        check: { type: 'shell', command },
        passed: r.exitCode === 0,
        output: r.stdout.trim() || r.stderr.trim() || `exit code ${r.exitCode}`,
    };
}
export function verifyFileExists(file) {
    const exists = existsSync(file);
    return {
        check: { type: 'file_exists', file },
        passed: exists,
        output: exists ? '文件存在' : `文件不存在: ${file}`,
    };
}
export function verifyFileContains(file, pattern) {
    try {
        const content = readFileSync(file, 'utf8');
        const regex = new RegExp(pattern);
        const matched = regex.test(content);
        return {
            check: { type: 'file_contains', file, pattern },
            passed: matched,
            output: matched ? `文件包含 "${pattern}"` : `文件不包含 "${pattern}"`,
        };
    }
    catch {
        return {
            check: { type: 'file_contains', file, pattern },
            passed: false,
            output: `无法读取文件: ${file}`,
        };
    }
}
async function verifyTestCount(minCount) {
    const r = await runShell(`bun test`, 60_000);
    // bun test 把进度打在 stderr，最终统计也常在 stderr，所以合并解析
    const combined = `${r.stdout}\n${r.stderr}`;
    if (r.errored) {
        return {
            check: { type: 'test_count', minCount },
            passed: false,
            output: '无法执行 bun test',
        };
    }
    const matches = [...combined.matchAll(/(\d+)\s+pass/gi)];
    const count = matches.length > 0 ? parseInt(matches[matches.length - 1][1], 10) : 0;
    const passed = count >= minCount;
    return {
        check: { type: 'test_count', minCount },
        passed,
        output: `${count} pass (需要 >=${minCount})`,
    };
}
export async function runVerification(checks) {
    if (checks.length === 0) {
        return { passed: true, details: [], summary: '无验证项' };
    }
    const results = [];
    for (const check of checks) {
        let result;
        switch (check.type) {
            case 'shell':
                result = await verifyShell(check.command ?? '', check.timeout ?? 30_000);
                break;
            case 'file_exists':
                result = verifyFileExists(check.file ?? '');
                break;
            case 'file_contains':
                result = verifyFileContains(check.file ?? '', check.pattern ?? '');
                break;
            case 'test_count':
                result = await verifyTestCount(check.minCount ?? 0);
                break;
            default:
                result = {
                    check,
                    passed: false,
                    output: `未知验证类型: ${check.type}`,
                };
        }
        results.push(result);
    }
    const allPassed = results.every((r) => r.passed);
    const failedNames = results.filter((r) => !r.passed).map((r) => formatCheckName(r.check));
    let summary;
    if (allPassed) {
        summary = `✅ 全部通过 (${results.length}/${results.length})`;
    }
    else {
        summary = `❌ 验证未通过: ${failedNames.join(', ')}`;
    }
    return { passed: allPassed, details: results, summary };
}
function formatCheckName(check) {
    switch (check.type) {
        case 'shell':
            return `shell(${(check.command ?? '').slice(0, 40)})`;
        case 'file_exists':
            return `exists(${check.file})`;
        case 'file_contains':
            return `contains(${check.file}:${check.pattern})`;
        case 'test_count':
            return `tests(>=${check.minCount})`;
        default:
            return check.type;
    }
}
