import { describe, expect, it } from 'vitest';
import type { FeatureSpec } from '../src/core/types';
import { allowedSymbols, planSuite, surfaceConformanceErrors } from '../src/tier1/planSuite';
import { applyTemplates, matchTemplate } from '../src/tier1/testgen';
import { parseExportSignature } from '../src/tier1/planSuite';

function calcSpec(): FeatureSpec {
  return {
    title: 'Calc', prompt: 'calc',
    features: [{
      id: 'F1', name: 'calc', description: 'adds', parentId: null,
      deps: ['F0'],
      files: ['src/calc.ts'], testFile: 'tests/f1.test.ts',
      edgeCases: ['empty input', 'zero'], envVars: [],
      exports: [{ file: 'src/calc.ts', symbol: 'calc' }],
      stubs: [{
        path: 'src/calc.ts',
        content: 'export function calc(a: number, b: number): number { throw new Error("stub"); }\n',
      }],
    }, {
      id: 'F0', name: 'adder', description: 'helper', parentId: null,
      deps: [], files: ['src/adder.ts'], testFile: 'tests/f0.test.ts',
      edgeCases: [], 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"); }\n',
      }],
    }],
  } as unknown as FeatureSpec;
}

describe('SET 2 planSuite + templates', () => {
  it('derives 1 behavior + N edges + 1 integration; imports from wiring', () => {
    const spec = calcSpec();
    const plan = planSuite(spec, spec.features[0]!);
    // 1 export + 2 edges + 1 dep = 4
    expect(plan.cases).toHaveLength(4);
    expect(plan.cases.filter(c => c.kind === 'behavior')).toHaveLength(1);
    expect(plan.cases.filter(c => c.kind === 'edge').map(c => c.name)).toEqual(['empty input', 'zero']);
    expect(plan.cases.filter(c => c.kind === 'integration')).toHaveLength(1);
    const syms = plan.imports.flatMap(i => i.symbols);
    expect(syms).toContain('calc');
    expect(syms).toContain('add');
  });

  it('templates fire only when edge text licenses the property', () => {
    const spec = calcSpec();
    const feature = spec.features[0]!;
    // License roundtrip on the string-export feature.
    feature.edgeCases = ['roundtrip', 'empty'];
    feature.exports = [{ file: 'src/calc.ts', symbol: 'norm' }];
    feature.stubs = [{
      path: 'src/calc.ts',
      content: 'export function norm(s: string): string { throw new Error("stub"); }\n',
    }];
    const plan = planSuite(spec, feature);
    const fill = applyTemplates(plan, feature.edgeCases);
    expect(fill.templated).toBeGreaterThanOrEqual(1);
    expect(fill.events.some(e => e.includes('roundtrip') || e.includes('rejects-empty'))).toBe(true);
    // Behavior export without a matching licensed template stays a hole.
    expect(fill.holes.some(h => h.kind === 'behavior')).toBe(true);
    expect(matchTemplate(plan.cases.find(c => c.kind === 'edge' && c.name === 'roundtrip')!, feature.edgeCases)?.id)
      .toBe('roundtrip-string');
  });

  it('surface conformance rejects undeclared call sites', () => {
    const spec = calcSpec();
    const plan = planSuite(spec, spec.features[0]!);
    const allowed = allowedSymbols(plan, spec.features[0]!);
    expect(surfaceConformanceErrors('expect(calc(1, 2)).toBe(3);', allowed)).toEqual([]);
    expect(surfaceConformanceErrors('expect(nope(1)).toBe(1);', allowed)[0]).toMatch(/undeclared symbol "nope"/);
  });
});
