/**
 * SET 72 — every LLM call gets 3 tries (Andy, 2026-07-27): the previously
 * one-shot call sites (born-red surgery, freeze-gate redraw, build draw,
 * entropy reset) walk the decorrelation ladder before giving up.
 */
import { describe, expect, it } from 'vitest';
import type { FeatureSpec, LlmClient, RepairInput } from '../src/core/types';
import { runTier1Job } from '../src/tier1/runner';
import { makeEvalSuiteRunner } from '../src/tier1/testExec';

const WRONG = 'export function add(a: number, b: number): number { return a - b; }';
const RIGHT = 'export function add(a: number, b: number): number { return a + b; }';
// Dynamic-eval poison — the build vet lint rejects this deterministically.
const EVIL = "export function add(a: number, b: number): number { return eval('a+b') as number; }";
const VACUOUS = 'expect(true).toBe(true);';
const REDBODY = 'expect(add(2, 3)).toBe(5);';

function makeSpec(): FeatureSpec {
  return {
    title: 'Adder', prompt: 'adder',
    features: [{
      id: 'F1', name: 'add', description: 'adds two numbers', parentId: null,
      deps: [], files: ['src/adder.ts'], testFile: 'tests/f1-add.test.ts',
      edgeCases: ['zero'], envVars: [],
      exports: [{ file: 'src/adder.ts', symbol: 'add' }],
      stubs: [{ path: 'src/adder.ts', content: `export function add(a: number, b: number): number { throw new Error('stub: not implemented'); }` }],
    }],
  } as unknown as FeatureSpec;
}

describe('SET 72 — 3 tries per LLM call', () => {
  it('born-red surgery: vacuous draws retry hotter; try 3 lands red — no anchor fallback', async () => {
    const surgeryTemps: (number | undefined)[] = [];
    const llm = {
      async encodeFeatures() { return makeSpec(); },
      async generateTests() { return { content: '', parts: { imports: [], cases: [] } }; },
      async fixTestCases(_s: unknown, _f: unknown, req: { name: string }[], _i: unknown, _im: unknown, o?: { purpose?: string; temperature?: number }) {
        if (o?.purpose?.startsWith('born-red surgery:')) {
          surgeryTemps.push(o.temperature);
          // Tries 1–2 stay vacuous; try 3 asserts behavior.
          const body = surgeryTemps.length >= 3 ? REDBODY : VACUOUS;
          return { cases: req.map(r => ({ name: r.name, kind: 'behavior' as const, body })) };
        }
        // Fill: every case vacuous — forces the born-red surgery path.
        return { cases: [{ name: req[0]!.name, kind: 'behavior' as const, body: VACUOUS }] };
      },
      async generateImplementation() { return [{ path: 'src/adder.ts', content: RIGHT }]; },
      async repair(_i: RepairInput) { return [{ path: 'src/adder.ts', content: RIGHT }]; },
    } as unknown as LlmClient;
    const res = await runTier1Job({ prompt: 'adder', llm, runSuite: makeEvalSuiteRunner() });
    const msgs = res.log.map(e => e.message);
    expect(surgeryTemps.length).toBe(3);
    expect(surgeryTemps).toEqual([0.3, 0.7, 1.0]); // the decorrelation ladder
    expect(msgs.some(m => /born-red surgery try [12]\/3: still vacuous — retrying hotter/.test(m))).toBe(true);
    // The suite froze on the model's red case, not the deterministic anchor.
    expect(msgs.some(m => /born-red: deterministic anchor frozen/.test(m))).toBe(false);
    expect(res.frozenTests[0]!.content).toContain(REDBODY);
    expect(res.problems).toEqual([]);
  });

  it('build draw: lint-rejected draws retry hotter; try 3 builds — feature never build-failed', async () => {
    const buildTemps: (number | undefined)[] = [];
    const llm = {
      async encodeFeatures() { return makeSpec(); },
      async generateTests() { return { content: '', parts: { imports: [], cases: [] } }; },
      async fixTestCases(_s: unknown, _f: unknown, req: { name: string }[]) {
        const name = req[0]!.name;
        return {
          cases: [{
            name,
            kind: (name === 'zero' ? 'edge' : 'behavior') as 'edge' | 'behavior',
            body: name === 'zero' ? 'expect(add(7, 0)).toBe(7);' : REDBODY,
          }],
        };
      },
      async generateImplementation(_s: FeatureSpec, _f: unknown, _t: unknown, _d: unknown, o?: { temperature?: number; purpose?: string }) {
        if (o?.purpose?.startsWith('build:')) {
          buildTemps.push(o.temperature);
          return [{ path: 'src/adder.ts', content: buildTemps.length >= 3 ? RIGHT : EVIL }];
        }
        return [{ path: 'src/adder.ts', content: RIGHT }];
      },
      async repair(_i: RepairInput) { return [{ path: 'src/adder.ts', content: RIGHT }]; },
    } as unknown as LlmClient;
    const res = await runTier1Job({ prompt: 'adder', llm, runSuite: makeEvalSuiteRunner() });
    const msgs = res.log.map(e => e.message);
    expect(buildTemps.length).toBe(3);
    expect(msgs.some(m => /build draw yielded 0 accepted files — retry [23]\/3/.test(m))).toBe(true);
    expect(msgs.some(m => /build-failed:/.test(m))).toBe(false);
    // SET 81 — build lines state the node's place in the shipment, not "1/1 file".
    expect(msgs.some(m => /^node 1 of 1 built — add \(src\/adder\.ts\)$/.test(m))).toBe(true);
    expect(res.problems).toEqual([]);
  });

  it('entropy reset: corpse reset retries hotter and heals on try 2', async () => {
    let resetCalls = 0;
    const llm = {
      async encodeFeatures() { return makeSpec(); },
      async generateTests() { return { content: '', parts: { imports: [], cases: [] } }; },
      async fixTestCases(_s: unknown, _f: unknown, req: { name: string }[]) {
        const name = req[0]!.name;
        return {
          cases: [{
            name,
            kind: (name === 'zero' ? 'edge' : 'behavior') as 'edge' | 'behavior',
            body: name === 'zero' ? 'expect(add(7, 0)).toBe(7);' : REDBODY,
          }],
        };
      },
      async generateImplementation(_s: FeatureSpec, _f: unknown, _t: unknown, _d: unknown, o?: { purpose?: string }) {
        if (o?.purpose?.startsWith('entropy reset:')) {
          resetCalls++;
          // Try 1 re-emits the incumbent (corpse); try 2 heals.
          return [{ path: 'src/adder.ts', content: resetCalls >= 2 ? RIGHT : WRONG }];
        }
        return [{ path: 'src/adder.ts', content: WRONG }];
      },
      // Stubborn repair — identical draw, corpse after first sighting.
      async repair(_i: RepairInput) { return [{ path: 'src/adder.ts', content: WRONG }]; },
    } as unknown as LlmClient;
    const res = await runTier1Job({
      prompt: 'adder', llm, runSuite: makeEvalSuiteRunner(),
      gates: { maxPasses: 8, stallLimit: 3, progressStall: 3 },
    });
    const msgs = res.log.map(e => e.message);
    expect(resetCalls).toBeGreaterThanOrEqual(2);
    expect(msgs.some(m => /corpse detected .*— reset retry 2\/3/.test(m))).toBe(true);
    expect(msgs.some(m => /^converged in [1-9]/.test(m))).toBe(true);
    expect(res.problems).toEqual([]);
  });
});
