import { describe, it, expect } from 'vitest';
import { routeTarget, deterministicFix, repairHint } from '../src/core/diagnostics';
import type { TscDiagnostic } from '../src/core/sandbox';

const d = (o: Partial<TscDiagnostic>): TscDiagnostic => ({
  file: 'src/print_renderer.ts', line: 1, code: 0, message: '', ...o,
});

describe('diagnostic oracle', () => {
  it('routes export-contract errors to the module they name, not the reporter', () => {
    const t = routeTarget(d({
      code: 2459,
      message: `Module '"./page_manager"' declares 'Page' locally, but it is not exported.`,
    }));
    expect(t).toEqual({ file: 'src/page_manager.ts', rerouted: true });
  });

  it('routes TS2305 missing-export to the named module (T3 IR-gate contract growth)', () => {
    const t = routeTarget(d({
      file: 'tests/f2.test.ts',
      code: 2305,
      message: `Module '"../src/revision_manager"' has no exported member 'RevisionManager'.`,
    }));
    expect(t).toEqual({ file: 'src/revision_manager.ts', rerouted: true });
  });

  it('leaves ordinary errors at the reporting file', () => {
    const t = routeTarget(d({ code: 2345, file: 'src/x.ts', message: 'type mismatch' }));
    expect(t).toEqual({ file: 'src/x.ts', rerouted: false });
  });

  it('deterministically adds export for TS2459 (declared but not exported)', () => {
    const content = `interface Page {\n  id: string;\n}\nexport function get() { throw new Error('stub'); }`;
    const out = deterministicFix(
      d({ code: 2459, message: `Module '"./page_manager"' declares 'Page' locally, but it is not exported.` }),
      content,
    );
    expect(out).toContain('export interface Page');
    // already-exported → no-op
    expect(deterministicFix(
      d({ code: 2459, message: `Module '"./page_manager"' declares 'Page' locally, but it is not exported.` }),
      out!,
    )).toBeNull();
  });

  it('applies the compiler\'s own "Did you mean" suggestion as a deterministic rename', () => {
    const content = `const u = createuser('a');\nexpect(u).toBeTruthy();`;
    const out = deterministicFix(
      d({ code: 2552, line: 1, message: `Cannot find name 'createuser'. Did you mean 'createUser'?` }),
      content,
    );
    expect(out).toBe(`const u = createUser('a');\nexpect(u).toBeTruthy();`);
  });

  it('returns null when there is no mechanical fix (semantic error)', () => {
    expect(deterministicFix(
      d({ code: 2345, message: `Argument of type 'string' is not assignable to parameter of type 'number'.` }),
      'foo(bar);',
    )).toBeNull();
  });

  it('deterministically removes an unused import specifier (TS6133)', () => {
    // middle specifier → drop just that name
    expect(deterministicFix(
      d({ code: 6133, line: 1, message: `'foo' is declared but its value is never read.` }),
      `import { a, foo, b } from './m';\nconst x = a + b;`,
    )).toBe(`import { a, b } from './m';\nconst x = a + b;`);
    // last specifier → drop the whole import line
    expect(deterministicFix(
      d({ code: 6133, line: 1, message: `'foo' is declared but its value is never read.` }),
      `import { foo } from './m';\nconst x = 1;`,
    )).toBe(`const x = 1;`);
    // unused LOCAL (not an import) → no mechanical fix, falls through to the LLM
    expect(deterministicFix(
      d({ code: 6133, line: 1, message: `'y' is declared but its value is never read.` }),
      `const y = 5;`,
    )).toBeNull();
  });

  it('gives a class-scoped hint that bounds the LLM', () => {
    expect(repairHint(d({ code: 2307, message: `Cannot find module './x'.` }))).toMatch(/import path/);
    // hints now come from the frozen oracle table
    expect(repairHint(d({ code: 2459, message: `Module '"./m"' declares 'X' locally, but it is not exported.` })))
      .toMatch(/export/i);
    // the additive (bidirectional) hint lives on the "member missing" codes
    expect(repairHint(d({ code: 2305, message: `Module '"./m"' has no exported member 'X'.` })))
      .toMatch(/ADD it/);
    expect(repairHint(d({ code: 999999, message: 'unknown' }))).toBe('');
  });
});
