/**
 * SET 71c — vacuous-abandonment breaker: a suite the model can only make
 * vacuous freezes the deterministic anchor contract instead of abandoning
 * and fanning dependency-uncovered across the tree.
 */
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 RIGHT = 'export function add(a: number, b: number): number { return a + b; }';
const VACUOUS = 'expect(true).toBe(true);';

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'); }` }],
      },
      {
        id: 'F2', name: 'double', description: 'doubles via add', parentId: null,
        deps: ['F1'], files: ['src/double.ts'], testFile: 'tests/f2-double.test.ts',
        edgeCases: [], envVars: [],
        exports: [{ file: 'src/double.ts', symbol: 'double' }],
        stubs: [{ path: 'src/double.ts', content: `export function double(a: number): number { throw new Error('stub'); }` }],
      },
    ],
  } as unknown as FeatureSpec;
}

describe('SET 71c — deterministic anchor on vacuous born-red', () => {
  it('freezes the anchor, keeps dependents covered, re-enqueues vacuous cases', async () => {
    const llm = {
      async encodeFeatures() { return makeSpec(); },
      async generateTests() { return { content: '', parts: { imports: [], cases: [] } }; },
      async generateImplementation(_s: FeatureSpec, f: { files: string[] }) {
        return [{ path: f.files[0]!, content: f.files[0]!.includes('double')
          ? 'export function double(a: number): number { return a + a; }'
          : RIGHT }];
      },
      async repair(i: RepairInput) { return (i.feature as { files: string[] }).files.map(p => ({ path: p, content: RIGHT })); },
      async fixTestCases(_s: unknown, f: { id?: string }, req: { name: string }[]) {
        // F1's model draws are ALWAYS vacuous (born-red can never go red);
        // F2 fills honestly.
        const name = req[0]!.name;
        const body = (f?.id === 'F1') ? VACUOUS
          : name.includes('double') || true ? 'expect(double(2)).toBe(4);' : VACUOUS;
        return { cases: [{ name, kind: 'behavior' as const, body }] };
      },
    } as unknown as LlmClient;

    const res = await runTier1Job({ prompt: 'adder', llm, runSuite: makeEvalSuiteRunner() });
    const msgs = res.log.map(e => e.message);

    // The breaker fired; F1 froze on the deterministic anchor.
    expect(msgs.some(m => /born-red: deterministic anchor frozen \("add anchor \(callable\)"/.test(m))).toBe(true);
    const f1Suite = res.frozenTests.find(t => t.path.includes('f1-add'))?.content ?? '';
    expect(f1Suite).toContain('not.toThrow');
    // NO abandonment, NO dependency fan-out.
    expect(msgs.some(m => /no-coverage — abandoned/.test(m))).toBe(false);
    expect(msgs.some(m => /dependency uncovered/.test(m))).toBe(false);
    // The vacuous cases went to the warm-world relay.
    expect(res.metrics.reEnqueuedCases).toBeGreaterThanOrEqual(1);
  });
});
