/**
 * SET 93 — stub-derived throw cases drop at born-red (doctrine 14 falsifier).
 *
 * A case whose entire contract is `.toThrow('stub: not implemented')` cannot be
 * de-vacuum'd by surgery. It must be dropped with disclosure before surgery,
 * and must never appear in a `vacuous case(s) → surgery` event.
 */
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';
import { isStubDerivedThrowCase } from '../src/tier1/bornRed';

function specWithStubDerivedThrow(): FeatureSpec {
  return {
    title: 't', prompt: 'p',
    features: [{
      id: 'F1', name: 'Model', description: 'Model class', parentId: null,
      deps: [], files: ['src/model.ts'], testFile: 'tests/model.test.ts',
      edgeCases: [], envVars: [],
      exports: [{ file: 'src/model.ts', symbol: 'Model' }],
      stubs: [{
        path: 'src/model.ts',
        content: `export class Model { constructor() { throw new Error('stub: not implemented'); } }`,
      }],
    }],
  } as unknown as FeatureSpec;
}

class ClassLlm {
  async encodeFeatures() { return specWithStubDerivedThrow(); }
  async generateTests() { return { content: '', parts: { imports: [], cases: [] } }; }
  async fixTestCases(_s: unknown, _f: unknown, req: { name: string }[]) {
    const n = req[0]!.name;
    return {
      cases: [{
        name: n,
        kind: 'behavior' as const,
        body: `expect(() => new Model()).toThrow('stub: not implemented');`,
      }],
    };
  }
  async generateImplementation() {
    return [{ path: 'src/model.ts', content: `export class Model {}` }];
  }
  async repair() {
    return [{ path: 'src/model.ts', content: `export class Model {}` }];
  }
}

describe('SET 93 — stub-derived throw cases drop at born-red', () => {
  it('predicate matches the sole stub-derived throw contract', () => {
    const spec = specWithStubDerivedThrow();
    const feature = spec.features[0]!;
    expect(isStubDerivedThrowCase(
      `expect(() => new Model()).toThrow('stub: not implemented');`,
      feature,
      spec,
    )).toBe(true);
    expect(isStubDerivedThrowCase(
      `expect(() => new Model()).not.toThrow();`,
      feature,
      spec,
    )).toBe(false);
  });

  it('never enters surgery; drop line emitted; counter increments', async () => {
    const res = await runTier1Job({
      prompt: 'p',
      llm: new ClassLlm() as unknown as LlmClient,
      runSuite: makeEvalSuiteRunner(),
    });
    const msgs = res.log.map(e => e.message);
    expect(msgs.some(m => /born-red: 1 stub-derived throw case\(s\) dropped/.test(m))).toBe(true);
    expect(res.metrics.bornRedStubDerivedDrops).toBeGreaterThanOrEqual(1);
    expect(msgs.some(m => /born-red: .* vacuous case\(s\) → surgery/.test(m))).toBe(false);
  });
});
