/**
 * SET 101d — transpile|<file> refused with "impl repair exhausted" suspends
 * that file's L2 ladder until a fresh draw advances the draw epoch.
 */
import { afterEach, describe, expect, it } from 'vitest';
import type { FeatureSpec, LlmClient } from '../src/core/types';
import { runTier1Job } from '../src/tier1/runner';
import {
  GATE_UNAVAILABLE,
  makeEvalSuiteRunner,
  setProgramParseProbe,
} from '../src/tier1/testExec';

/** Unhealable within parser-guided ≤3 edits — stays a transpile placeholder. */
const BROKEN = [
  'export function add(a: number, b: number): number {',
  '  return @@@NOT_TS@@@ (((( {{{ ;;;',
  '  ??? invalid ???',
  '',
].join('\n');

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'); }",
      }],
    }],
  } as unknown as FeatureSpec;
}

/** Class double: build lands unparseable; repair would be L2 if not suspended. */
class QuarantineLlm {
  l2Purposes: string[] = [];

  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);' : 'expect(add(2, 3)).toBe(5);',
      }],
    };
  }
  async generateImplementation() {
    return [{ path: 'src/adder.ts', content: BROKEN }];
  }
  async repair(_input: unknown, o?: { purpose?: string }) {
    if (/^L2 procedure:/.test(o?.purpose ?? '')) {
      this.l2Purposes.push(o?.purpose ?? '');
    }
    return [{ path: 'src/adder.ts', content: BROKEN }];
  }
}

describe('SET 101d — quarantine terminal', () => {
  afterEach(() => {
    setProgramParseProbe(null);
  });

  it('suspends L2 for transpile file after impl repair exhausted', async () => {
    // Build mode allows transpile placeholders; program-parse fail-open lets them land.
    setProgramParseProbe(async () => GATE_UNAVAILABLE);

    const llm = new QuarantineLlm();
    const res = await runTier1Job({
      prompt: 'adder',
      llm: llm as unknown as LlmClient,
      runSuite: makeEvalSuiteRunner(),
      // shellLock would fall back to stub bodies and erase the unparseable draw.
      gates: { maxPasses: 4, stallLimit: 3, progressStall: 4, shellLock: false },
    });

    expect(res.log.some(e =>
      /repair suspended: src\/adder\.ts awaiting fresh draw \(impl repair exhausted\)/.test(e.message),
    )).toBe(true);
    expect(res.metrics.quarantineSuspensions).toBeGreaterThanOrEqual(1);
    // No L2 procedure loop while the draw epoch is still the refused one.
    expect(llm.l2Purposes).toHaveLength(0);
    expect(res.metrics.l2Draws).toBe(0);
  });
});
