/**
 * SET 95c — canary: a freeform response (full describe/it boilerplate
 * instead of a ⟨HOLE⟩…⟨/END⟩ block) is rejected as `overreach|body` at the
 * primary fill site — it never becomes the case body.
 */
import { describe, expect, it } from 'vitest';
import type { FeatureSpec, LlmClient } from '../src/core/types';
import { runTier1Job } from '../src/tier1/runner';
import { makeEvalSuiteRunner } from '../src/tier1/testExec';

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: [], 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'); }`,
      }],
    }],
  };
}

const FREEFORM_BOILERPLATE =
  "describe('add', () => { it('adds', () => { expect(add(2, 3)).toBe(5); }); });";

/** Class-based LlmClient: fixTestCases always reproduces full boilerplate. */
class OverreachLlm implements Partial<LlmClient> {
  async encodeFeatures() {
    return makeSpec();
  }
  async generateTests() {
    return { content: '', parts: { imports: [], cases: [] } };
  }
  async generateImplementation() {
    return [{ path: 'src/adder.ts', content: 'export function add(a: number, b: number): number { return a + b; }' }];
  }
  async repair() {
    return [];
  }
  async fixTestCases(
    _spec: unknown,
    _feature: unknown,
    req: { name: string; kind: 'behavior' | 'edge' | 'integration' }[],
  ) {
    const target = req[0]!;
    return { cases: [{ name: target.name, kind: target.kind, body: FREEFORM_BOILERPLATE }] };
  }
}

describe('SET 95c — freeform boilerplate rejected as overreach|body', () => {
  it('increments overreachBodyRejections and never freezes the freeform body verbatim', async () => {
    const llm = new OverreachLlm() as unknown as LlmClient;
    const res = await runTier1Job({
      prompt: 'adder',
      llm,
      runSuite: makeEvalSuiteRunner(),
      gates: { holeOnlyFill: true, grammarEnabled: false },
    });

    expect(res.metrics.overreachBodyRejections).toBeGreaterThanOrEqual(1);
    expect(res.log.some(e => /rejected overreach\|body: no hole marker in response/.test(e.message))).toBe(true);

    const suite = res.frozenTests.find(t => t.path.includes('f1-add'))?.content ?? '';
    expect(suite).not.toContain(FREEFORM_BOILERPLATE);
  });
});

/**
 * SET 95d — the fill site was the 95c canary; born-red surgery is a
 * NON-FILL fixTestCases call site that got the same three-step contract
 * rolled to it. The mock answers hole-only-clean at the primary fill draw
 * (so the case is accepted, just vacuous against the throwing stub — no
 * case fails, so born-red surgery fires), then answers full freeform
 * boilerplate once surgery re-targets the still-vacuous case.
 */
const VACUOUS_HOLE_BODY = "⟨HOLE⟩\nexpect(typeof add).toBe('function');\n⟨/END⟩";
const SURGERY_FREEFORM_BOILERPLATE =
  "describe('add', () => { it('adds', () => { expect(add(2, 3)).toBe(5); }); });";

class SurgeryOverreachLlm implements Partial<LlmClient> {
  async encodeFeatures() {
    return makeSpec();
  }
  async generateTests() {
    return { content: '', parts: { imports: [], cases: [] } };
  }
  async generateImplementation() {
    return [{ path: 'src/adder.ts', content: 'export function add(a: number, b: number): number { return a + b; }' }];
  }
  async repair() {
    return [];
  }
  async fixTestCases(
    _spec: unknown,
    _feature: unknown,
    req: { name: string; kind: 'behavior' | 'edge' | 'integration'; errors: string[] }[],
  ) {
    const target = req[0]!;
    const isSurgery = target.errors.some(e => e.includes('no implementation'));
    return {
      cases: [{
        name: target.name,
        kind: target.kind,
        body: isSurgery ? SURGERY_FREEFORM_BOILERPLATE : VACUOUS_HOLE_BODY,
      }],
    };
  }
}

describe('SET 95d — non-fill site (born-red surgery) rejects freeform overreach', () => {
  it('a vacuous fill escalates to surgery, whose freeform redraw is rejected as overreach|body', async () => {
    const llm = new SurgeryOverreachLlm() as unknown as LlmClient;
    const res = await runTier1Job({
      prompt: 'adder',
      llm,
      runSuite: makeEvalSuiteRunner(),
      gates: { holeOnlyFill: true, grammarEnabled: false },
    });

    // The primary fill draw was hole-only-clean and accepted (not an overreach).
    // Every rejection recorded here comes from the born-red-surgery redraws.
    expect(res.metrics.overreachBodyRejections).toBeGreaterThanOrEqual(1);
    expect(res.log.some(e =>
      /^case "add" rejected overreach\|body: no hole marker in response$/.test(e.message),
    )).toBe(true);

    // The freeform surgery redraw never became the case body.
    const suite = res.frozenTests.find(t => t.path.includes('f1-add'))?.content ?? '';
    expect(suite).not.toContain(SURGERY_FREEFORM_BOILERPLATE);
  });
});
