import { describe, it } from "vitest";
import assert from "node:assert/strict";
import { join } from "node:path";
import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import {
  appendBenchHistory,
  buildBenchDetectionSeries,
  buildBenchHistoryRecord,
  getBenchGoalProgress,
  getBenchHistoryPath,
  getBenchHistoryStats,
  readBenchHistory,
  type BenchHistoryRecord,
} from "./bench-history.ts";

const EMPTY_BY_SIGNAL = { paintTree: 0, computedStyle: 0, forcedState: 0, visual: 0 };

function makePrescannerSummary(
  overrides: Partial<NonNullable<BenchHistoryRecord["prescanner"]>> = {},
): NonNullable<BenchHistoryRecord["prescanner"]> {
  return {
    total: 0,
    detected: 0,
    craterResolved: 0,
    chromiumFallbacks: 0,
    chromiumDetected: 0,
    passedAfterFallback: 0,
    metadataOnly: 0,
    craterBySignal: { ...EMPTY_BY_SIGNAL },
    ...overrides,
  };
}

function makeRecord(overrides: Partial<BenchHistoryRecord> = {}): BenchHistoryRecord {
  return {
    runId: "2026-04-02T00:00:00.000Z",
    fixture: "page",
    backend: "chromium",
    trials: 10,
    startSeed: 1,
    elapsedMs: 6000,
    avgMsPerTrial: 600,
    llmEnabled: false,
    strict: false,
    suggestApproval: false,
    approvalPath: undefined,
    visualDetected: 8,
    computedDetected: 4,
    hoverDetected: 1,
    paintTreeDetected: 0,
    a11yDetected: 0,
    eitherDetected: 9,
    neitherDetected: 1,
    detectionRate: 0.9,
    metadataOnly: null,
    prescanner: null,
    ...overrides,
  };
}

describe("getBenchHistoryPath", () => {
  it("stores benchmark history under data", () => {
    assert.match(getBenchHistoryPath(), /data\/bench-history\.jsonl$/);
  });
});

describe("buildBenchHistoryRecord", () => {
  it("derives avgMsPerTrial and detectionRate", () => {
    const record = buildBenchHistoryRecord({
      runId: "2026-04-02T00:00:00.000Z",
      fixture: "page",
      backend: "prescanner",
      trials: 5,
      startSeed: 11,
      elapsedMs: 1880,
      llmEnabled: false,
      strict: true,
      suggestApproval: false,
      approvalPath: "/repo/approval.json",
      visualDetected: 3,
      computedDetected: 1,
      hoverDetected: 1,
      paintTreeDetected: 3,
      a11yDetected: 0,
      eitherDetected: 5,
      neitherDetected: 0,
      prescanner: makePrescannerSummary({
        total: 5,
        detected: 5,
        craterResolved: 3,
        chromiumFallbacks: 2,
        chromiumDetected: 2,
        passedAfterFallback: 0,
        metadataOnly: 2,
        craterBySignal: { paintTree: 2, computedStyle: 1, forcedState: 0, visual: 0 },
      }),
    });

    assert.equal(record.avgMsPerTrial, 376);
    assert.equal(record.detectionRate, 1);
    assert.equal(record.prescanner?.craterResolved, 3);
    assert.equal(record.metadataOnly, 2, "metadataOnly is lifted from the prescanner summary");
  });

  it("leaves metadataOnly null when there is no prescanner data", () => {
    const record = buildBenchHistoryRecord({
      runId: "2026-04-02T00:00:00.000Z",
      fixture: "page",
      backend: "chromium",
      trials: 5,
      startSeed: 11,
      elapsedMs: 1000,
      llmEnabled: false,
      strict: false,
      suggestApproval: false,
      visualDetected: 1,
      computedDetected: 0,
      hoverDetected: 0,
      paintTreeDetected: 0,
      a11yDetected: 0,
      eitherDetected: 1,
      neitherDetected: 4,
    });

    assert.equal(record.metadataOnly, null);
  });
});

describe("bench-history round trip", () => {
  it("appends and reads jsonl records", async () => {
    const dir = await mkdtemp(join(tmpdir(), "bench-history-"));
    const historyPath = join(dir, "bench-history.jsonl");
    try {
      await appendBenchHistory([
        makeRecord({ backend: "chromium" }),
        makeRecord({ backend: "prescanner", prescanner: makePrescannerSummary({ total: 10, detected: 9, craterResolved: 4, chromiumFallbacks: 6, chromiumDetected: 5, passedAfterFallback: 1 }) }),
      ], historyPath);

      const loaded = await readBenchHistory(historyPath);
      assert.equal(loaded.length, 2);
      assert.equal(loaded[0].backend, "chromium");
      assert.equal(loaded[1].backend, "prescanner");
    } finally {
      await rm(dir, { recursive: true, force: true });
    }
  });
});

describe("getBenchHistoryStats", () => {
  it("summarizes latest by backend and comparable speedups", () => {
    const stats = getBenchHistoryStats([
      makeRecord({
        runId: "2026-04-01T00:00:00.000Z",
        backend: "chromium",
        elapsedMs: 9366,
        avgMsPerTrial: 624.4,
        trials: 15,
        startSeed: 1,
      }),
      makeRecord({
        runId: "2026-04-01T00:05:00.000Z",
        backend: "prescanner",
        elapsedMs: 5638,
        avgMsPerTrial: 375.8667,
        trials: 15,
        startSeed: 1,
        prescanner: makePrescannerSummary({ total: 15, detected: 15, craterResolved: 7, chromiumFallbacks: 8, chromiumDetected: 8, passedAfterFallback: 0 }),
      }),
      makeRecord({
        runId: "2026-04-02T00:00:00.000Z",
        backend: "chromium",
        fixture: "dashboard",
        elapsedMs: 7000,
        avgMsPerTrial: 700,
      }),
    ]);

    assert.equal(stats.totalRuns, 3);
    assert.equal(stats.byBackend.get("chromium")?.count, 2);
    assert.equal(stats.byBackend.get("prescanner")?.latest.avgMsPerTrial, 375.8667);
    assert.equal(stats.comparableSpeedups.length, 1);
    assert.equal(stats.comparableSpeedups[0]?.fixture, "page");
    assert.equal(stats.comparableSpeedups[0]?.trials, 15);
    assert.equal(stats.comparableSpeedups[0]?.speedup.toFixed(2), "1.66");
  });
});

describe("buildBenchDetectionSeries", () => {
  it("returns the latest filtered points in chronological order for charting", () => {
    const series = buildBenchDetectionSeries([
      makeRecord({
        runId: "2026-04-03T00:00:00.000Z",
        backend: "prescanner",
        fixture: "page",
        trials: 10,
        eitherDetected: 7,
        detectionRate: 0.7,
        avgMsPerTrial: 410,
      }),
      makeRecord({
        runId: "2026-04-01T00:00:00.000Z",
        backend: "prescanner",
        fixture: "page",
        trials: 10,
        eitherDetected: 6,
        detectionRate: 0.6,
        avgMsPerTrial: 430,
      }),
      makeRecord({
        runId: "2026-04-02T00:00:00.000Z",
        backend: "chromium",
        fixture: "page",
        trials: 10,
        eitherDetected: 9,
        detectionRate: 0.9,
        avgMsPerTrial: 690,
      }),
      makeRecord({
        runId: "2026-04-04T00:00:00.000Z",
        backend: "prescanner",
        fixture: "dashboard",
        trials: 10,
        eitherDetected: 8,
        detectionRate: 0.8,
        avgMsPerTrial: 390,
      }),
    ], {
      backend: "prescanner",
      fixture: "page",
      limit: 1,
    });

    assert.equal(series.total, 2);
    assert.equal(series.points.length, 1);
    assert.equal(series.points[0]?.runId, "2026-04-03T00:00:00.000Z");
    assert.equal(series.points[0]?.detected, 7);
    assert.equal(series.points[0]?.detectionRate, 0.7);
    assert.equal(series.points[0]?.avgMsPerTrial, 410);
  });

  it("keeps limited points ascending after selecting the newest records", () => {
    const series = buildBenchDetectionSeries([
      makeRecord({ runId: "2026-04-01T00:00:00.000Z", detectionRate: 0.5 }),
      makeRecord({ runId: "2026-04-03T00:00:00.000Z", detectionRate: 0.7 }),
      makeRecord({ runId: "2026-04-02T00:00:00.000Z", detectionRate: 0.6 }),
    ], {
      limit: 2,
    });

    assert.equal(series.total, 3);
    assert.deepEqual(series.points.map((point) => point.runId), [
      "2026-04-02T00:00:00.000Z",
      "2026-04-03T00:00:00.000Z",
    ]);
  });
});

describe("getBenchGoalProgress", () => {
  it("reports prescanner detection and speedup gaps against targets", () => {
    const stats = getBenchHistoryStats([
      makeRecord({
        runId: "2026-04-01T00:00:00.000Z",
        backend: "chromium",
        elapsedMs: 9000,
        avgMsPerTrial: 900,
        trials: 10,
        startSeed: 1,
        eitherDetected: 10,
        detectionRate: 1,
      }),
      makeRecord({
        runId: "2026-04-01T00:05:00.000Z",
        backend: "prescanner",
        elapsedMs: 4500,
        avgMsPerTrial: 450,
        trials: 10,
        startSeed: 1,
        eitherDetected: 7,
        detectionRate: 0.7,
        prescanner: makePrescannerSummary({
          total: 10,
          detected: 7,
          craterResolved: 4,
          chromiumFallbacks: 6,
          chromiumDetected: 3,
          passedAfterFallback: 0,
        }),
      }),
    ]);

    const progress = getBenchGoalProgress(stats, {
      prescannerDetectionRate: 0.8,
      prescannerSpeedup: 3,
    });

    assert.equal(progress.prescannerDetection?.latestRate, 0.7);
    assert.equal(progress.prescannerDetection?.gap, 0.1);
    assert.equal(progress.prescannerDetection?.passed, false);
    assert.equal(progress.prescannerSpeedup?.latestSpeedup, 2);
    assert.equal(progress.prescannerSpeedup?.gap, 1);
    assert.equal(progress.prescannerSpeedup?.passed, false);
  });
});
