/**
 * SET 57e — the exported run log carries the 57a counters and the three
 * function ratings under `metrics`, from the same formula source as the UI.
 */
import { describe, expect, it } from 'vitest';
import { buildRunLogJson, buildRunLogTxt } from '../web/src/exportRunLog';
import { REPORT_COUNTER_KEYS } from '../src/tier1/statsAggregate';

const INPUT = {
  id: 'run-1',
  ts: '2026-07-27T10:00:00.000Z',
  model: 'mock',
  events: [{ ts: '2026-07-27T10:00:00.000Z', phase: 'encode', featureId: null, message: 'encoding' }],
  problems: [],
  metrics: {
    elapsedMs: 1000,
    featuresTotal: 4, featuresFrozenWithoutRedraw: 3, irRedraws: 1,
    nodesTotal: 4, nodesBuiltCleanFirstPass: 2,
    boundedLoopNodes: 2, boundedLoopIterations: 5,
    candidatesDrawn: 12, candidatesAccepted: 3,
    errorsTotal: 2, errorsParse: 1, errorsConformance: 1, errorsExecution: 0, errorsCrash: 0,
    deferredTotal: 1, deferredRelitigated: 1, deferredResolvedAfterRelitigation: 1, deferredPermanent: 0,
    nodesHealed: 2, fixesUnsuccessful: 3, fixesUnparseable: 1,
  },
};

describe('SET 57e — export passthrough', () => {
  it('.json metrics carry every 57a counter plus the three scores', () => {
    const parsed = JSON.parse(buildRunLogJson(INPUT)) as { metrics: Record<string, number> };
    for (const k of REPORT_COUNTER_KEYS) {
      expect(parsed.metrics[k], k).toBe((INPUT.metrics as Record<string, number>)[k]);
    }
    expect(parsed.metrics.scoreIr).toBeCloseTo(3 / 4);
    expect(parsed.metrics.scoreBuild).toBeCloseTo(2 / 4);
    expect(parsed.metrics.scoreRha).toBeCloseTo(2 / 2);
  });

  it('.log.txt metrics block includes the scores', () => {
    const txt = buildRunLogTxt(INPUT);
    expect(txt).toContain('"scoreIr"');
    expect(txt).toContain('"scoreBuild"');
    expect(txt).toContain('"scoreRha"');
    expect(txt).toContain('"boundedLoopIterations": 5');
  });
});
