/**
 * SET 95d — the build/generateImplementation call site is a non-fill site
 * that also carries the `holeOnlyFill` gate. `generateImplementation`'s
 * contract stays whole-file `GeneratedFile[]` (see `isBuildOverreach` in
 * `src/tier1/frame.ts`), so the enforced predicate is content-level: an
 * implementation file must never carry test-scaffolding tokens
 * (`describe(`/`it(`/`test(`). shell-lock is disabled in these tests so the
 * raw (unlocked) draw reaches the overreach check — shell-lock's own frame
 * reconstruction would otherwise strip an appended `describe(...)` call
 * before the gate ever saw it, which would make the test tautological.
 */
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 LEAKING_BUILD_DRAW =
  "export function add(a: number, b: number): number { return a + b; }\n"
  + "describe('leaked test scaffold', () => { it('should not be here', () => {}); });";

/** Class-based LlmClient: fill answers hole-only-clean; build leaks a describe/it call. */
class BuildOverreachLlm implements Partial<LlmClient> {
  async encodeFeatures() {
    return makeSpec();
  }
  async generateTests() {
    return { content: '', parts: { imports: [], cases: [] } };
  }
  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: '⟨HOLE⟩\nexpect(add(2, 3)).toBe(5);\n⟨/END⟩' }],
    };
  }
  async generateImplementation() {
    return [{ path: 'src/adder.ts', content: LEAKING_BUILD_DRAW }];
  }
  async repair() {
    return [];
  }
}

describe('SET 95d — build-site overreach gate (non-fill call site)', () => {
  it('rejects a generateImplementation draw that leaks test-scaffold tokens', async () => {
    const llm = new BuildOverreachLlm() as unknown as LlmClient;
    const res = await runTier1Job({
      prompt: 'adder',
      llm,
      runSuite: makeEvalSuiteRunner(),
      gates: { holeOnlyFill: true, grammarEnabled: false, shellLock: false },
    });

    expect(res.metrics.overreachBodyRejections).toBeGreaterThanOrEqual(1);
    expect(res.log.some(e =>
      /rejected overreach\|body: test-scaffold token in implementation file/.test(e.message),
    )).toBe(true);

    // The leaking draw was never adopted into the built artifact.
    const built = res.files.find(f => f.path === 'src/adder.ts');
    expect(built?.content.includes('describe(')).not.toBe(true);
  });

  it('ships default-OFF: the same leaking draw is adopted unchanged when the gate is off', async () => {
    const llm = new BuildOverreachLlm() as unknown as LlmClient;
    const res = await runTier1Job({
      prompt: 'adder',
      llm,
      runSuite: makeEvalSuiteRunner(),
      gates: { grammarEnabled: false, shellLock: false },
    });

    expect(res.metrics.overreachBodyRejections).toBe(0);
    const built = res.files.find(f => f.path === 'src/adder.ts');
    expect(built?.content.includes('describe(')).toBe(true);
  });
});
