import { existsSync, readFileSync } from 'fs';
import { join } from 'path';
import { homedir } from 'os';
import yaml from 'js-yaml';
import { z } from 'zod';
export const WorkflowStepSchema = z.object({
    name: z.string(),
    // 'address' is the legacy name — normalized to 'fix' at parse time for backward compat
    type: z.enum(['review', 'fix', 'recheck', 'address', 'conflict-resolve']).transform(t => t === 'address' ? 'fix' : t),
    reviewer: z.enum(['auto', 'claude', 'codex', 'origin']).default('auto'),
    when: z.string().optional(),
    max_rounds: z.number().int().positive().default(1),
    instructions: z.string().optional(),
});
export const WorkflowSchema = z.object({
    on: z.array(z.string()).default(['opened', 'synchronize']),
    steps: z.array(WorkflowStepSchema),
});
export const DEFAULT_REVIEW_INSTRUCTIONS = [
    '## Constraints',
    '- Do not run tsc, ts-node, or build commands — inspect source files directly with git diff/log.',
    '- Do not install packages or modify lock files.',
    '## Output format',
    'Structure your output as: ## Summary, ## Critical Issues, ## Warnings, ## Suggestions.',
    'Be concise. Skip praise.',
    '## Verdict (required — machine-parsed)',
    'The very last line of your response MUST be exactly one of these three lines.',
    'Do not add bold, italics, punctuation, headers, or any other text after it:',
    'VERDICT: APPROVE',
    'VERDICT: NEEDS WORK',
    'VERDICT: BLOCK',
    '',
    'Use APPROVE for no issues or trivial nits only.',
    'Use NEEDS WORK for addressable issues that are not blocking.',
    'Use BLOCK for security risks, data loss, broken API contracts, or correctness bugs.',
].join('\n');
export const DEFAULT_FIX_INSTRUCTIONS = [
    'Only fix issues explicitly called out in the review.',
    'Do not refactor unrelated code, rename variables, or add tests unless specifically requested.',
    'If a comment requires deeper understanding of business logic, skip it.',
].join('\n');
export const DEFAULT_RECHECK_INSTRUCTIONS = [
    'Check that every issue flagged in the original review has been addressed.',
    'If all issues are resolved, output VERDICT: APPROVE.',
    'If issues remain, repeat the original verdict (NEEDS WORK or BLOCK) and list what is still outstanding.',
    'Do not flag new issues — focus only on resolution of the originals.',
].join('\n');
export const DEFAULT_CONFLICT_RESOLVE_INSTRUCTIONS = [
    'Resolve all merge conflict markers (<<<<<<< HEAD, =======, >>>>>>> branch).',
    'Keep meaningful changes from both sides when they do not contradict.',
    'When both sides modify the same line, prefer the incoming branch changes unless they break existing logic.',
    'Do not change any code outside of conflict regions.',
].join('\n');
// Default pipeline: review → fix issues (fix skipped when verdict is APPROVE).
// Execution always goes through this constant — there is no legacy direct-call path.
export const DEFAULT_WORKFLOW = [
    {
        name: 'review',
        type: 'review',
        reviewer: 'auto',
        max_rounds: 1,
        instructions: DEFAULT_REVIEW_INSTRUCTIONS,
    },
    {
        name: 'fix',
        type: 'fix',
        reviewer: 'origin',
        when: "review.verdict != 'APPROVE'",
        max_rounds: 1,
        instructions: DEFAULT_FIX_INSTRUCTIONS,
    },
];
export function loadWorkflow(operatorDir) {
    // Only look in operator-controlled directories — never inside the PR checkout.
    // Loading workflow config from untrusted PR code would let a PR hijack the runner.
    // Priority: project-local → global user config → DEFAULT_WORKFLOW.
    const candidates = [
        ...(operatorDir ? [join(operatorDir, '.crosscheck', 'workflow.yml')] : []),
        join(homedir(), '.crosscheck', 'workflow.yml'),
    ];
    for (const path of candidates) {
        if (!existsSync(path))
            continue;
        try {
            const raw = yaml.load(readFileSync(path, 'utf8'));
            return WorkflowSchema.parse(raw).steps;
        }
        catch {
            // Malformed workflow file — fall through to default
        }
    }
    return DEFAULT_WORKFLOW;
}
// Evaluates simple "stepName.field op 'value'" expressions.
// Unparseable expressions default to true (run the step).
export function evaluateWhen(expr, results) {
    const m = expr.trim().match(/^(\w+)\.(\w+)\s*(==|!=|>=|<=|>|<)\s*(?:'([^']*)'|(null|-?\d+(?:\.\d+)?))$/);
    if (!m)
        return true;
    const [, stepName, field, op, strVal, rawVal] = m;
    const result = results[stepName];
    if (!result)
        return true;
    const actual = result[field];
    const expected = strVal !== undefined ? strVal : rawVal === 'null' ? null : Number(rawVal);
    switch (op) {
        case '==': return actual === expected;
        case '!=': return actual !== expected;
        case '>': return Number(actual) > Number(expected);
        case '<': return Number(actual) < Number(expected);
        case '>=': return Number(actual) >= Number(expected);
        case '<=': return Number(actual) <= Number(expected);
        default: return true;
    }
}
//# sourceMappingURL=workflow.js.map