/**
 * SET 39 — TOTAL error taxonomy: catalog classifier + stratum attempt paths.
 */
import { describe, expect, it } from 'vitest';
import type { FeatureSpec, GeneratedFile } from '../src/core/types';
import {
  classifyCataloged,
  ERROR_CATALOG,
  formatCatalogDriftTripwire,
} from '../src/tier1/errorCatalog.generated';
import {
  attemptMechanicalFix,
  classifyFailure,
  formatAttemptStatus,
} from '../src/tier1/substrate';
import { strategyOrderForClass } from '../src/tier1/solutionStrata';

const FIXTURES: { message: string; expectTemplate?: string; expectClassPrefix?: string }[] = [
  { message: 'Unexpected token \'}\'', expectClassPrefix: 'crash|token' },
  { message: 'Invalid or unexpected token', expectClassPrefix: 'crash|token' },
  { message: 'Unexpected end of input', expectClassPrefix: 'crash|token' },
  { message: 'Invalid regular expression: missing /', expectClassPrefix: 'regex-literal' },
  { message: "Identifier 'Foo' has already been declared", expectClassPrefix: 'duplicate-decl' },
  { message: 'slugify is not defined', expectClassPrefix: 'undefined-symbol|slugify' },
  { message: 'parse is not a function', expectClassPrefix: 'is-not-a-function|parse' },
  { message: "Cannot read properties of null (reading 'id')", expectClassPrefix: 'runtime|nullread' },
  { message: 'Maximum call stack size exceeded', expectClassPrefix: 'runtime|max-stack' },
  { message: 'expect(...).toBe failed — actual: 1, expected: 5', expectClassPrefix: 'assertion|toBe' },
  { message: 'expect(...).toThrow failed — actual: (fn)', expectClassPrefix: 'throw-mismatch' },
  { message: 'unresolved import ./addr (from src/calc)', expectClassPrefix: 'unresolved-import' },
  { message: 'transpile src/a.ts: Unexpected token', expectClassPrefix: 'transpile|src/a.ts' },
  { message: 'dynamic-eval: eval( is forbidden', expectClassPrefix: 'dynamic-eval' },
  { message: 'error TS2304: Cannot find name \'Foo\'.', expectClassPrefix: 'undefined-symbol|TS2304' },
  { message: 'error TS1005: \';\' expected.', expectClassPrefix: 'transpile|TS1005' },
  { message: 'stub: not implemented', expectClassPrefix: 'runtime|stub-not-implemented' },
];

describe('SET 39a/b error catalog totality', () => {
  it('ships a non-empty frozen catalog covering the four emitter families', () => {
    expect(ERROR_CATALOG.length).toBeGreaterThan(80);
    const sources = new Set(ERROR_CATALOG.map(t => t.source));
    for (const s of ['v8-syntax', 'v8-runtime', 'typescript', 'assertion', 'harness'] as const) {
      expect(sources.has(s)).toBe(true);
    }
  });

  it('classifyCataloged maps known fixtures; unknown list empty', () => {
    const unknown: string[] = [];
    for (const f of FIXTURES) {
      const c = classifyCataloged(f.message);
      if (c.templateId === 'catalog-drift') unknown.push(f.message);
      else if (f.expectClassPrefix) {
        expect(c.classKey.startsWith(f.expectClassPrefix) || c.classKey === f.expectClassPrefix
          || c.classKey.includes(f.expectClassPrefix.split('|')[0]!), f.message).toBe(true);
      }
      expect(c.stratum === 'catalog-drift' || ['mechanical-first', 'playbook', 'semantic-procedure'].includes(c.stratum as string)).toBe(true);
    }
    expect(unknown).toEqual([]);
  });

  it('unmatched messages are catalog-drift with the tripwire string', () => {
    const msg = 'zzzxqwy totally novel emitter noise 99999';
    const c = classifyCataloged(msg);
    expect(c).toMatchObject({ templateId: 'catalog-drift', classKey: 'catalog-drift', stratum: 'catalog-drift' });
    expect(formatCatalogDriftTripwire(msg)).toBe(`catalog-drift: "${msg}" matches no template`);
  });

  it('classifyFailure attaches catalog templateId/stratum', () => {
    const c = classifyFailure('t', 'expect(...).toBe failed — actual: 1, expected: 2');
    expect(c.classKey).toBe('assertion|toBe');
    expect(c.templateId).toBeTruthy();
    expect(c.stratum).toBe('semantic-procedure');
  });
});

describe('SET 39b attempt paths abolish no-handler', () => {
  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;

  it('assertion → cataloged (stratum: semantic-procedure), never no-handler', () => {
    const attempt = attemptMechanicalFix(
      classifyFailure('t', 'expect(...).toBe failed — actual: 1, expected: 2'),
      artifact, suite, spec,
    );
    expect(attempt.status).toBe('cataloged');
    expect(attempt.status).not.toBe('no-handler');
    expect(formatAttemptStatus(attempt)).toMatch(/cataloged \(stratum: semantic-procedure\)/);
  });

  it('true unknown → catalog-drift tripwire reason', () => {
    const msg = 'zzzxqwy totally novel emitter noise 99999';
    const failure = classifyFailure('t', msg);
    const attempt = attemptMechanicalFix(failure, artifact, suite, spec);
    // Freeform runtime slice may still classify as runtime|* with catalog-drift template.
    if (failure.templateId === 'catalog-drift' || failure.kind === 'catalog-drift') {
      expect(attempt.status === 'catalog-drift' || attempt.status === 'cataloged').toBe(true);
    }
    expect(formatCatalogDriftTripwire(msg)).toMatch(/matches no template/);
  });
});

describe('SET 39 / 37b solution strata order', () => {
  it('strategyOrderForClass ranks by observed resolution rate', () => {
    const mock = strategyOrderForClass('undefined-symbol|jest');
    expect(mock[0]).toBe('mechanical');
    expect(mock.indexOf('draws')).toBeGreaterThan(mock.indexOf('mechanical'));

    const assertion = strategyOrderForClass('assertion|toBe');
    expect(assertion[0]).toBe('surgery'); // 0.70 when invoked
    expect(assertion.indexOf('draws')).toBeLessThan(assertion.indexOf('mechanical'));
  });
});
