/**
 * SET 37c/37d — substrate half: mock-API refuse-model + empty-draw classify.
 */
import { describe, expect, it, vi } from 'vitest';
import type { FeatureSpec, GeneratedFile } from '../src/core/types';
import {
  classifyCaseClade,
  isMethodAnchorName,
  METHOD_ANCHOR_CLADE,
  methodAnchorBody,
  rebindCladeBody,
} from '../src/tier1/clades';
import {
  attemptMechanicalFix,
  classifyFailure,
  formatEmptyDraws,
  refusesModelDraw,
} from '../src/tier1/substrate';

const suite: GeneratedFile = {
  path: 'tests/f1.test.ts',
  content: "import { describe, it, expect } from 'vitest';\ndescribe('x', () => { it('y', () => { expect(1).toBe(1); }); });\n",
};
const artifact: GeneratedFile[] = [
  { path: 'src/a.ts', content: 'export function slugify(s: string): string { return s; }\n' },
];
const spec = { title: 't', prompt: 't', features: [] } as FeatureSpec;

describe('SET 37c mock-API refuse-model', () => {
  it('pins refusesModelDraw for jest|spyOn|fn classKeys', () => {
    expect(refusesModelDraw('undefined-symbol|jest')).toBe(true);
    expect(refusesModelDraw('is-not-a-function|spyOn')).toBe(true);
    expect(refusesModelDraw('undefined-symbol|fn')).toBe(true);
    expect(refusesModelDraw('undefined-symbol|slugify')).toBe(false);
    expect(refusesModelDraw('assertion|toBe')).toBe(false);
  });

  it('substrate attempt never requests model (model call count = 0)', () => {
    let modelCalls = 0;
    const applyWithModel = vi.fn(() => {
      modelCalls++;
      return null;
    });

    for (const msg of ['jest is not defined', 'fn is not defined', 'spyOn is not a function']) {
      const failure = classifyFailure('t', msg);
      expect(refusesModelDraw(failure.classKey)).toBe(true);
      const attempt = attemptMechanicalFix(failure, artifact, suite, spec);
      // Mechanical path only: fixed or refused — never cataloged→model, never no-handler shrug.
      expect(['fixed', 'refused']).toContain(attempt.status);
      if (!refusesModelDraw(failure.classKey)) applyWithModel();
    }
    expect(modelCalls).toBe(0);
    expect(applyWithModel).not.toHaveBeenCalled();
  });
});

describe('SET 37d empty-draw + method-anchor', () => {
  it('classifies unparseable at "(empty)" as distinct empty-draw', () => {
    const a = classifyFailure('registerUser', 'dropped — unparseable: Unexpected end of input at "(empty)"');
    expect(a.classKey).toBe('empty-draw');
    expect(a.kind).toBe('empty-draw');

    const b = classifyFailure('t', '(empty)');
    expect(b.classKey).toBe('empty-draw');

    const c = classifyFailure('t', 'model returned empty');
    expect(c.classKey).toBe('empty-draw');
  });

  it('formatEmptyDraws excludes empty draws from attempt wording', () => {
    expect(formatEmptyDraws(0)).toBe('0 empty draws');
    expect(formatEmptyDraws(1)).toBe('1 empty draw');
    expect(formatEmptyDraws(3)).toBe('3 empty draws');
  });

  it('empty-draw attempt is cataloged mechanical — not a model draw', () => {
    const failure = classifyFailure('createArticle', 'unparseable at "(empty)"');
    const attempt = attemptMechanicalFix(failure, artifact, suite, spec);
    expect(attempt.status).toBe('cataloged');
    expect(attempt.stratum).toBe('mechanical-first');
    expect(attempt.reason).toMatch(/empty-draw/);
  });

  it('recognizes method-anchor clade helpers (pure; runner is Integration)', () => {
    for (const n of ['registerUser', 'createArticle', 'checkRateLimit']) {
      expect(isMethodAnchorName(n)).toBe(true);
      expect(classifyCaseClade({ name: n, kind: 'behavior', body: '' }).family).toBe(METHOD_ANCHOR_CLADE);
    }
    expect(isMethodAnchorName('adds numbers')).toBe(false);
    const body = methodAnchorBody('registerUser', 'user object');
    expect(body).toContain('registerUser(');
    expect(body).toContain('toBeDefined');
    const rebound = rebindCladeBody({
      name: 'createArticle',
      kind: 'behavior',
      body: '',
      clade: { branch: 'contract', family: METHOD_ANCHOR_CLADE, bindings: { symbol: 'createArticle' } },
    });
    expect(rebound).toContain('createArticle(');
  });
});
