import { describe, expect, it } from "vitest";
import { addCaptureTiming, addCardReadyTiming, addScrollReadyTiming, clampScanLimit, clampSkipRows, emptyAutoScanStats, resolveScanTargetCount, updateScanTiming } from "./scannerSession";

describe("scannerSession helpers", () => {
  it("clamps scan target counts into a sane range", () => {
    expect(clampScanLimit(0)).toBe(1);
    expect(clampScanLimit(2500)).toBe(1800);
  });

  it("respects the manual scan limit and only caps it against the detected inventory count", () => {
    expect(resolveScanTargetCount(16, 124)).toBe(16);
    expect(resolveScanTargetCount(200, 124)).toBe(124);
    expect(resolveScanTargetCount(16, 0)).toBe(16);
    expect(resolveScanTargetCount(16, null)).toBe(16);
  });

  it("keeps skipped rows within the visible grid range", () => {
    expect(clampSkipRows(-5)).toBe(0);
    expect(clampSkipRows(99)).toBe(8);
  });

  it("updates scan timing and projects the 100-artifact run", () => {
    const stats = { ...emptyAutoScanStats, parsed: 4, verified: 2 };
    addCaptureTiming(stats, { totalMs: 1200, ocrMs: 900 }, 1500);
    addCaptureTiming(stats, { totalMs: 800, ocrMs: 500 }, 1000);
    updateScanTiming(stats, 1000, 9000);
    expect(stats.elapsedMs).toBe(8000);
    expect(stats.activeScanMs).toBe(8000);
    expect(stats.averageMsPerParsed).toBe(2000);
    expect(stats.activeAverageMsPerParsed).toBe(2000);
    expect(stats.artifactsPerMinute).toBe(30);
    expect(stats.activeArtifactsPerMinute).toBe(30);
    expect(stats.projectedMsFor100).toBe(200000);
    expect(stats.activeProjectedMsFor100).toBe(200000);
    expect(stats.captureMs).toBe(2000);
    expect(stats.captureRoundTripMs).toBe(2500);
    expect(stats.averageCaptureRoundTripMs).toBe(1250);
    expect(stats.captureRoundTripOverheadMs).toBe(500);
    expect(stats.averageCaptureRoundTripOverheadMs).toBe(250);
    expect(stats.ocrMs).toBe(1400);
    expect(stats.averageCaptureMs).toBe(1000);
    expect(stats.averageOcrMs).toBe(700);
    expect(stats.captureP50Ms).toBe(800);
    expect(stats.captureP90Ms).toBe(1200);
    expect(stats.ocrP50Ms).toBe(500);
    expect(stats.ocrP90Ms).toBe(900);
  });

  it("tracks detail-card and scroll readiness timing separately from OCR", () => {
    const stats = { ...emptyAutoScanStats, parsed: 2, verified: 2 };
    addCardReadyTiming(stats, 180);
    addCardReadyTiming(stats, 220);
    addScrollReadyTiming(stats, 320);
    updateScanTiming(stats, 1000, 3000);
    expect(stats.cardReadyMs).toBe(400);
    expect(stats.cardReadyCount).toBe(2);
    expect(stats.averageCardReadyMs).toBe(200);
    expect(stats.scrollReadyMs).toBe(320);
    expect(stats.scrollReadyCount).toBe(1);
    expect(stats.averageScrollReadyMs).toBe(320);
  });

  it("preserves active scan timing when final elapsed time includes queued write flush", () => {
    const stats = { ...emptyAutoScanStats, parsed: 5, verified: 5, activeScanMs: 4000, writeFlushMs: 900 };
    updateScanTiming(stats, 1000, 6000, { preserveActiveScanMs: true });
    expect(stats.elapsedMs).toBe(5000);
    expect(stats.activeScanMs).toBe(4000);
    expect(stats.writeFlushMs).toBe(900);
    expect(stats.averageMsPerParsed).toBe(1000);
    expect(stats.activeAverageMsPerParsed).toBe(800);
    expect(stats.projectedMsFor100).toBe(100000);
    expect(stats.activeProjectedMsFor100).toBe(80000);
  });

  it("keeps active scan timing growing during the live scan phase", () => {
    const stats = { ...emptyAutoScanStats, parsed: 2, verified: 2, activeScanMs: 800 };
    updateScanTiming(stats, 1000, 5000);
    expect(stats.elapsedMs).toBe(4000);
    expect(stats.activeScanMs).toBe(4000);
    expect(stats.activeAverageMsPerParsed).toBe(2000);
  });
});
