/**
 * SET 64d — runtime trace: literal-call extraction, deterministic replay at
 * the symbol boundary, bounded page formatting. 0 tokens.
 */
import { describe, expect, it } from 'vitest';
import {
  buildTracePage,
  extractTraceCalls,
  isLiteralArgs,
  TRACE_CAP,
  traceEligible,
} from '../src/tier1/traceCapture';
import { traceSymbolCalls } from '../src/tier1/testExec';

const CASE_BODY = [
  'const out = normalize(" Hello ");',
  'expect(add(2, 3)).toBe(5);',
  'expect(add(7, 0)).toBe(7);',
  'expect(add(compute(), 1)).toBe(2);', // nested call — must be skipped
  'expect(add(x, 2)).toBe(4);', // free identifier — must be skipped
  "expect(fmt({ n: 3, label: 'three' })).toBe('3 three');",
].join('\n');

describe('SET 64d — runtime trace', () => {
  it('literal-args filter admits literals and object keys, rejects calls/identifiers', () => {
    expect(isLiteralArgs('2, 3')).toBe(true);
    expect(isLiteralArgs("{ n: 3, label: 'three' }")).toBe(true);
    expect(isLiteralArgs('[1, 2], true, null')).toBe(true);
    expect(isLiteralArgs('compute()')).toBe(false);
    expect(isLiteralArgs('x, 2')).toBe(false);
  });

  it('extraction is deduped, ordered, and literal-only', () => {
    const calls = extractTraceCalls(CASE_BODY, ['add', 'fmt']);
    expect(calls).toEqual([
      { symbol: 'add', argsSrc: '2, 3' },
      { symbol: 'add', argsSrc: '7, 0' },
      { symbol: 'fmt', argsSrc: "{ n: 3, label: 'three' }" },
    ]);
  });

  it('replay captures return and thrown values against the real module', () => {
    const files = [{
      path: 'src/add.ts',
      content: [
        'export function add(a: number, b: number): number {',
        "  if (b === 0) throw new Error('div-adjacent superstition');",
        '  return a - b;',
        '}',
      ].join('\n'),
    }];
    const lines = traceSymbolCalls(files, 'src/add.ts', extractTraceCalls(CASE_BODY, ['add']));
    expect(lines).not.toBeNull();
    expect(lines![0]).toEqual({ call: 'add(2, 3)', outcome: '→ -1' });
    expect(lines![1]!.outcome).toMatch(/threw .*superstition/);
    const page = buildTracePage(lines);
    expect(page).toContain('add(2, 3) → -1');
    expect(page!.length).toBeLessThanOrEqual(TRACE_CAP + 120);
  });

  it('eligibility: semantic-procedure classes only', () => {
    expect(traceEligible('assertion')).toBe(true);
    expect(traceEligible('throw-mismatch')).toBe(true);
    expect(traceEligible('catalog-drift')).toBe(true);
    expect(traceEligible('unresolved-import')).toBe(false);
    expect(traceEligible('crash')).toBe(false);
  });

  it('empty calls → null page (no noise)', () => {
    expect(traceSymbolCalls([], 'src/x.ts', [])).toBeNull();
    expect(buildTracePage(null)).toBeNull();
    expect(buildTracePage([])).toBeNull();
  });
});
