#!/usr/bin/env node
import fs from "node:fs/promises";
import path from "node:path";
import vm from "node:vm";
import { createRequire } from "node:module";
import { fileURLToPath } from "node:url";

const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const require = createRequire(import.meta.url);
const {
  splitRaceMonitorCommands,
  parseRaceMonitorLine,
  applyRaceMonitorCommand,
  buildViews
} = require(path.join(root, "src", "raceMonitor.js"));
const { parseTickerCsv, normalizeTickerRows } = require(path.join(root, "src", "myRacePass.js"));
const { createRaceIntelligence, deriveRaceState } = require(path.join(root, "src", "raceIntelligence.js"));

const failures = [];
const notes = [];

function fail(message) {
  failures.push(message);
}

function assert(condition, message) {
  if (!condition) fail(message);
}

function sampleRaceMonitorState() {
  return {
    source: "racemonitor",
    raw: [],
    classes: {},
    competitors: new Map(),
    roster: new Map(),
    session: "",
    className: "",
    trackName: "",
    trackLength: "",
    weatherLocation: "",
    mode: "",
    clock: "",
    lapsToGo: "",
    raceTime: "",
    timeToGo: "",
    flag: "",
    flagCode: "",
    flagLabel: "",
    lastUpdated: null
  };
}

function applyRaceMonitorSample(message) {
  const state = sampleRaceMonitorState();
  const commands = splitRaceMonitorCommands(message);
  for (const raw of commands) {
    const command = parseRaceMonitorLine(raw);
    if (command) applyRaceMonitorCommand(state, command);
  }
  state.lastUpdated = new Date().toISOString();
  return buildViews(state);
}

function checkRaceMonitorParser() {
  const qualifying = applyRaceMonitorSample(`$RMDTL,""
$B,1,"602 LM--Qualifying"
$C,1,"602 LM"
$COMP,"101","24",1,"Trevor","Hudson","NC"," "
$COMP,"102","42",1,"Mason","Carter","VA"," "
$COMP,"103","22",1,"Logan","Pierce","IL"," "
$H,1,"101",3,"00:00:15.112"
$H,2,"102",3,"00:00:15.244"
$H,3,"103",3,"00:00:15.301"
$J,"101","00:00:15.112","00:00:45.100"
$J,"102","00:00:15.244","00:00:45.500"
$J,"103","00:00:15.301","00:00:45.900"
$RMS,"qualifying"
$E,"TRACKNAME","Freedom 75"
$F,0,"00:00:00","21:53:51","00:00:00","Green"`);

  assert(qualifying.competitors.length === 3, "Race Monitor qualifying sample should create three drivers.");
  assert(qualifying.qualifyingOrder[0]?.displayNumber === "24", "Race Monitor qualifying order should put fastest driver first.");
  assert(qualifying.meta.className === "602 LM", "Race Monitor class name should be cleaned and available.");
  assert(qualifying.meta.mode === "qualifying", "Race Monitor mode should be qualifying.");

  const race = applyRaceMonitorSample(`$RMDTL,""
$B,1,"602 LM Feature"
$C,1,"602 LM"
$COMP,"101","24",1,"Trevor","Hudson","NC"," "
$COMP,"102","42",1,"Mason","Carter","VA"," "
$COMP,"103","22",1,"Logan","Pierce","IL"," "
$G,1,"101",12,"00:03:05.100"
$G,2,"102",12,"00:03:06.450"
$G,3,"103",11,"00:03:07.200"
$J,"101","00:00:15.220","00:03:05.100"
$J,"102","00:00:15.310","00:03:06.450"
$J,"103","00:00:15.400","00:03:07.200"
$RMS,"race"
$E,"TRACKNAME","Freedom 75"
$F,8,"00:00:00","22:10:12","00:00:00","Yellow"`);

  assert(race.runningOrder[0]?.displayNumber === "24", "Race Monitor race order should keep the leader first.");
  assert(race.runningOrder[2]?.gap === "-1 LAP", "Race Monitor race gap should show lap-down cars.");
  assert(race.meta.lapsToGo === "8", "Race Monitor laps-to-go should be available.");
  assert(race.meta.flagLabel === "Yellow", "Race Monitor yellow flag should be available.");

  const cleanup = applyRaceMonitorSample(`$RMDTL,""
$B,1,"GT Feature"
$C,1,"GT"
$COMP,"201","76",1,"VIKTOR","CECH","2019 Porsche GT2 RS Clubsport"," "
$G,1,"201",1,"00:00:15.100"
$J,"201","00:00:15.100","00:00:15.100"
$RMS,"race"`);
  assert(cleanup.competitors[0]?.name === "Viktor Cech", "Race Monitor names should collapse whitespace and normalize all-caps names.");
  assert(cleanup.competitors[0]?.hometown === "", "Race Monitor vehicle descriptions should not be used as hometowns.");
}

function checkMyRacePassParser() {
  const csv = `Track,TrackCity,TrackState,Class,Race,LapsToGo,Position1,Number1,FirstName1,LastName1,LastLapTime1,BestLapTime1,Diff1,Position2,Number2,FirstName2,LastName2,LastLapTime2,BestLapTime2,Diff2
Freedom 75,Elizabeth City,NC,602 LM,Feature,14,1,24,Trevor,Hudson,15.220,15.112,,2,42,Mason,Carter,15.310,15.244,-1.350`;
  const rows = parseTickerCsv(csv);
  const normalized = normalizeTickerRows(rows);
  assert(rows.length === 1, "MyRacePass CSV parser should read the sample row.");
  assert(normalized.meta.trackName === "Freedom 75", "MyRacePass track name should be available.");
  assert(normalized.meta.weatherLocation === "Elizabeth City, NC", "MyRacePass weather location should use track city/state.");
  assert(normalized.meta.mode === "race", "MyRacePass feature/laps-to-go sample should detect race mode.");
  assert(normalized.competitors.length === 2, "MyRacePass numbered slots should become drivers.");
  assert(normalized.competitors[1]?.gap === "-1.350", "MyRacePass driver gap should be preserved.");

  const cleanupCsv = `Track,Class,Race,LapsToGo,Position1,Number1,FirstName1,LastName1,Hometown1,LastLapTime1,BestLapTime1
Freedom 75,GT,Feature,8,1,76,VIKTOR,CECH,2019 Porsche GT2 RS Clubsport,15.100,15.000`;
  const cleanup = normalizeTickerRows(parseTickerCsv(cleanupCsv));
  assert(cleanup.competitors[0]?.firstName === "Viktor", "MyRacePass first names should normalize all-caps names.");
  assert(cleanup.competitors[0]?.lastName === "Cech", "MyRacePass last names should normalize all-caps names.");
  assert(cleanup.competitors[0]?.hometown === "", "MyRacePass vehicle descriptions should not be used as hometowns.");

  const colorCsv = `Track,Class,Race,LapsToGo,Position1,Number1,FirstName1,LastName1,Hometown1,Position2,Number2,FirstName2,LastName2,Hometown2
Freedom 75,GT,Feature,8,1,76,Sam,Reed,Blue/Silver,2,12,Ana,Cole,"Elizabeth City, NC"`;
  const colors = normalizeTickerRows(parseTickerCsv(colorCsv));
  assert(colors.competitors[0]?.hometown === "", "MyRacePass car colors should not be used as hometowns.");
  assert(colors.competitors[1]?.hometown === "Elizabeth City, NC", "MyRacePass real hometowns should pass the car-color filter.");
}

function checkRaceIntelligenceProfiles() {
  const intelligence = createRaceIntelligence();
  intelligence.profileStore.profiles = {
    "general|43": {
      number: "43",
      name: "Wrong General",
      firstName: "Wrong",
      lastName: "General",
      className: "",
      seenCount: 50,
      lastSeen: new Date().toISOString()
    },
    "t2|43": {
      number: "43",
      name: "Right Driver",
      firstName: "Right",
      lastName: "Driver",
      className: "T2",
      seenCount: 2,
      lastSeen: new Date().toISOString()
    },
    "general|99": {
      number: "99",
      name: "Low Count",
      firstName: "Low",
      lastName: "Count",
      className: "",
      seenCount: 1,
      lastSeen: new Date().toISOString()
    }
  };
  const view = {
    meta: { className: "T2", source: "test", session: "Feature", mode: "race", lastUpdated: new Date().toISOString() },
    competitors: [
      { number: "43", name: "Car 43", className: "T2", position: 1 },
      { number: "99", name: "Car 99", className: "T2", position: 2 }
    ],
    runningOrder: [
      { number: "43", name: "Car 43", className: "T2", position: 1 },
      { number: "99", name: "Car 99", className: "T2", position: 2 }
    ],
    raceOrder: [],
    qualifyingOrder: [],
    lineup: [],
    results: []
  };
  const enhanced = intelligence.enhance(view);
  assert(enhanced.runningOrder[0]?.name === "Right Driver", "Driver profile fallback should prefer class-scoped profiles over general number matches.");
  assert(enhanced.runningOrder[1]?.name === "Car 99", "Low-count profile fallback should not replace generated driver names.");
}

function checkRacePhaseDetection() {
  // Number("") is 0; an absent laps-to-go must not read as "0 to go" and
  // declare a time-based race finished while it is live (found against a
  // live Race Monitor kart feed, 2026-06-11).
  const rows = [
    { number: "24", name: "Trevor Hudson", position: 1, laps: 12, lastLap: "00:00:15.220" },
    { number: "42", name: "Mason Carter", position: 2, laps: 12, lastLap: "00:00:15.310" }
  ];
  const baseMeta = { session: "Feature", mode: "race", lastUpdated: new Date().toISOString() };
  const liveView = { meta: { ...baseMeta, lapsToGo: "" }, competitors: rows, runningOrder: rows };
  assert(deriveRaceState(liveView).phase === "live", "Empty laps-to-go must not read as a finished race.");
  const finishedView = { meta: { ...baseMeta, lapsToGo: "0" }, competitors: rows, runningOrder: rows };
  assert(deriveRaceState(finishedView).phase === "finished", "An explicit zero laps-to-go should still finish the race.");
}

function checkLapsLed() {
  const intelligence = createRaceIntelligence();
  const raceState = { phase: "live", mode: "race" };
  const viewWithLaps = laps => {
    const rows = [
      { number: "24", name: "Trevor Hudson", position: 1, laps },
      { number: "42", name: "Mason Carter", position: 2, laps, interval: "2.500" }
    ];
    return {
      meta: { className: "GT", source: "test", session: "Feature", mode: "race", lastUpdated: new Date().toISOString() },
      competitors: rows,
      runningOrder: rows,
      raceOrder: [],
      qualifyingOrder: [],
      lineup: [],
      results: []
    };
  };
  intelligence.buildStory(viewWithLaps(10), raceState);
  intelligence.buildStory(viewWithLaps(11), raceState);
  intelligence.buildStory(viewWithLaps(11), raceState);
  const story = intelligence.buildStory(viewWithLaps(12), raceState);
  assert(story.lapsLed && story.lapsLed.number === "24" && story.lapsLed.laps === 2,
    `Laps led should count each leader lap advance exactly once (got ${JSON.stringify(story.lapsLed)}).`);
}

function checkBattleWatch() {
  const intelligence = createRaceIntelligence();
  const rows = [
    { number: "24", name: "Trevor Hudson", position: 1 },
    { number: "42", name: "Mason Carter", position: 2, interval: "0.412", gap: "0.412" },
    { number: "7", name: "Logan Pierce", position: 3, interval: "4.210" }
  ];
  const view = {
    meta: { className: "T2", source: "test", session: "Feature", mode: "race", lastUpdated: new Date().toISOString() },
    competitors: rows,
    runningOrder: rows,
    raceOrder: [],
    qualifyingOrder: [],
    lineup: [],
    results: []
  };
  const raceState = { phase: "live", mode: "race" };
  intelligence.buildStory(view, raceState);
  let story = intelligence.buildStory(view, raceState);
  assert(!story.items.some(item => item.type === "battle"), "Battle watch should not fire before the streak threshold.");
  const session = [...intelligence.sessionHistory.values()][0];
  session.battleStreaks.forEach(entry => { entry.firstAt -= 3000; });
  story = intelligence.buildStory(view, raceState);
  const battleItem = story.items.find(item => item.type === "battle");
  assert(battleItem && /battle for the lead/i.test(battleItem.title), "A sustained sub-0.6s interval should surface a lead battle story.");
  assert(battleItem && battleItem.detail.includes("vs"), "Battle stories should name both drivers.");
  assert(battleItem && battleItem.aheadNumber === "24" && battleItem.behindNumber === "42", "Battle stories should carry the machine-readable pair numbers for row marking.");
  assert(!story.items.some(item => item.type === "closest-battle"), "The one-shot closest-battle item should stand down while a sustained battle is live.");

  // Lap-crossing tolerance: the leader finishes each lap first, so a
  // nose-to-tail pair reads "-1 LAP" for a beat every lap; the streak (and a
  // live battle) must survive the blip.
  const blippedRows = rows.map(driver => ({ ...driver }));
  blippedRows[1] = { ...blippedRows[1], interval: "-1 LAP", gap: "-1 LAP" };
  const blippedView = { ...view, competitors: blippedRows, runningOrder: blippedRows };
  const blippedStory = intelligence.buildStory(blippedView, raceState);
  assert(blippedStory.items.some(item => item.type === "battle"), "Battle stories must survive the one-beat -1 LAP blip at the line.");
  const resumedStory = intelligence.buildStory(view, raceState);
  assert(resumedStory.items.some(item => item.type === "battle"), "Battle streaks must resume cleanly after a lap-crossing blip.");

  const qualifying = intelligence.buildStory(view, { phase: "live", mode: "qualifying" });
  assert(!qualifying.items.some(item => item.type === "battle"), "Battle watch should not fire during qualifying.");

  // Regression guard: applyProfiles once cached enhanced driver copies across
  // lists, wiping list-specific interval/gap fields from runningOrder.
  const enhanced = intelligence.enhance(view);
  assert(enhanced.runningOrder?.[1]?.interval === "0.412", "enhance must preserve list-specific interval/gap fields.");
}

async function checkLiveMotionHelpers() {
  // app.js is a browser script; run it in a VM context with just enough DOM
  // stubbed for its load-time style application, then exercise the live-motion
  // trackers (position trends, fastest lap) that the graphics share.
  const appSource = await fs.readFile(path.join(root, "public", "app.js"), "utf8");
  const noopClassList = { add() {}, remove() {}, toggle() {}, contains: () => false };
  const stubElement = () => ({
    style: { setProperty() {} },
    classList: { ...noopClassList },
    appendChild() {},
    addEventListener() {}
  });
  const sandbox = {
    window: { addEventListener() {}, dispatchEvent() {} },
    document: {
      documentElement: stubElement(),
      body: stubElement(),
      addEventListener() {},
      createElement: stubElement,
      querySelectorAll: () => []
    },
    localStorage: { getItem: () => null, setItem() {}, removeItem() {} },
    location: { pathname: "/deep-check" },
    setTimeout,
    clearTimeout,
    setInterval,
    clearInterval,
    console
  };
  sandbox.window.document = sandbox.document;
  try {
    vm.createContext(sandbox);
    vm.runInContext(appSource, sandbox, { filename: "app.js" });
  } catch (err) {
    fail(`app.js should load in the deep-check sandbox: ${err.message}`);
    return;
  }

  const meta = { session: "Feature", className: "GT" };
  const firstTick = [
    { number: "24", name: "Trevor Hudson", position: 1, bestLap: "00:00:15.112" },
    { number: "42", name: "Mason Carter", position: 2, bestLap: "00:00:15.244" }
  ];
  sandbox.trackPositionTrends(firstTick, { meta });
  assert(sandbox.positionTrend(firstTick[0]) === null, "First sighting of a driver should not raise a position trend.");

  const swappedTick = [
    { number: "42", name: "Mason Carter", position: 1, bestLap: "00:00:15.244" },
    { number: "24", name: "Trevor Hudson", position: 2, bestLap: "00:00:15.112" }
  ];
  sandbox.trackPositionTrends(swappedTick, { meta });
  assert(sandbox.positionTrend(swappedTick[0])?.dir === "up", "A driver gaining a position should trend up.");
  assert(sandbox.positionTrend(swappedTick[1])?.dir === "down", "A driver losing a position should trend down.");

  sandbox.trackFastestLap({ meta, competitors: firstTick });
  assert(sandbox.fastestLapFlash() === null, "The first timed laps should set the session benchmark without flashing.");
  sandbox.trackFastestLap({
    meta,
    competitors: [
      firstTick[0],
      { number: "42", name: "Mason Carter", position: 1, bestLap: "00:00:14.998" }
    ]
  });
  const flash = sandbox.fastestLapFlash();
  assert(flash?.number === "42", "Improving the session-best lap should flash the new holder.");
  assert(flash?.timeText === "14.998", "The fastest-lap flash should carry the formatted lap time.");

  sandbox.trackFastestLap({ meta: { session: "Heat 2", className: "GT" }, competitors: firstTick });
  assert(sandbox.fastestLapFlash() === null, "A session change should clear the fastest-lap flash and benchmark.");
  sandbox.trackPositionTrends(swappedTick, { meta: { session: "Heat 2", className: "GT" } });
  assert(sandbox.positionTrend(swappedTick[0]) === null, "A session change should clear remembered positions.");
}

async function checkPackageSource() {
  const serverSource = await fs.readFile(path.join(root, "server.js"), "utf8");
  const controlPage = await fs.readFile(path.join(root, "public", "control.html"), "utf8");
  const controlScript = await fs.readFile(path.join(root, "public", "control.js"), "utf8");
  assert(controlPage.includes('src="/control.js'), "Control page should load its script from control.js.");
  const controlSource = controlPage + controlScript;
  const preflightSource = await fs.readFile(path.join(root, "public", "preflight.html"), "utf8");
  const routerSource = await fs.readFile(path.join(root, "public", "program-router.js"), "utf8");
  const bannerSource = await fs.readFile(path.join(root, "public", "top-banner.html"), "utf8");
  const launcherSource = await fs.readFile(path.join(root, "Raceworks.js"), "utf8");
  await fs.access(path.join(root, "scripts", "pre-race-check.mjs"));

  assert(launcherSource.includes('require("./server")'), "Launcher should start Raceworks.js through server.js.");
  assert(serverSource.includes('"leaders", "story", "weather"'), "Server should keep the top banner when Current Leaders, Race Story, or Weather are selected.");
  assert(serverSource.includes("raceMonitorConnectGeneration") && serverSource.includes("myRacePassConnectGeneration"), "Timing connects must be generation-guarded so rapid connects cannot leak orphan sockets or poll intervals.");
  assert(serverSource.includes("liveRaceMonitorSocket !== socket") && /onmessage[\s\S]{0,200}liveRaceMonitorSocket !== socket/.test(serverSource), "Race Monitor onmessage must ignore (and close) untracked sockets.");
  assert(routerSource.includes('new Set(["leaders", "story", "weather"])'), "Program router should treat Current Leaders, Race Story, and Weather as top-banner companion graphics.");
  assert(routerSource.includes("opacity = \"0\"") && routerSource.includes("opacity = \"1\""), "Program router should fade temporary companion graphics in and out.");
  assert(routerSource.includes("hideBannerCompanionGraphic({ animate: true })"), "Program router should fade top-banner companion graphics out instead of removing them abruptly.");
  assert(routerSource.includes('params.get("companion") === "1"') && routerSource.includes("!isCompanionFrame"), "Temporary companion frames should not run the full Program Overlay router.");
  assert(routerSource.includes("programUrl(graphic, version, { companion: true })"), "Top-banner companion graphics should be loaded as passive companion frames.");
  assert(routerSource.includes("!isProgramLive && !isCompanionFrame"), "Temporary companion frames should not start standalone overlay watchers.");
  assert(routerSource.includes('companionFrame.style.transition = "none"'), "Temporary companion frames should not fade in as a full-screen layer.");
  assert(routerSource.includes("frame.onload") && routerSource.includes('frame.style.opacity = "1"'), "Companion frames must stay invisible until their document loads (a navigating iframe paints white).");
  assert(controlSource.includes("scheduleTemporaryOutputReturn") && controlSource.includes("}, 5000);"), "Control page should auto-clear Race Story, Current Leaders, and Weather after 5 seconds when the top banner is up.");
  assert(controlSource.includes('const returnGraphic = topBannerVisible ? "topbanner"'), "Control page should return temporary companion graphics to the Top Banner when the banner is active.");
  assert(controlSource.includes("preserveTopBannerOutput") && controlSource.includes("shouldKeepTopBannerDuringTemporaryOutput"), "Control page should preserve the Top Banner while temporary companion graphics are sent.");
  assert(controlSource.includes("window.lrwSelectOutput = selectPreviewGraphic"), "Control page should expose preview selection for output buttons.");
  assert(controlSource.includes("window.lrwSendOutput = sendPreviewGraphic"), "Control page should expose send selection for output buttons.");
  assert(preflightSource.includes("Pre-Race Check") && preflightSource.includes("/api/health"), "Pre-race diagnostic page should be available.");
  assert(controlSource.includes('document.querySelector("#takeProgram").onclick'), "Take Program button should be wired.");
  assert(controlSource.includes("clearProgramButton.onclick"), "Clear Output button should be wired.");
  assert(controlSource.includes("postJson(\"/api/vmix/browser\"") && controlSource.includes(".then((vmixResult)"), "vMix browser sends should run without blocking output selection.");
  assert(bannerSource.includes('if (style.bannerRaceMode === "auto") return sessionDefault;'), "Top banner Auto mode should use qualifying/race metric defaults.");
  assert(bannerSource.includes("setInterval(() =>") && bannerSource.includes("sponsorLogoIndex += 1"), "Top banner should rotate selected sponsor logos.");
  assert(bannerSource.includes("flag-yellow") && bannerSource.includes("flag-red") && bannerSource.includes("flag-final"), "Top banner should expose yellow, red, and final flag classes.");
  assert(bannerSource.includes("finished") && bannerSource.includes("checkered"), "Top banner should recognize finished/checkered flag text.");
  assert(serverSource.includes("flagLabel") && bannerSource.includes("flagClass(meta)"), "Top banner should consume timing flag data from the server.");
  const styleSheets = [
    "styles-1-foundation.css",
    "styles-2-control-theme.css",
    "styles-3-graphics-podium.css",
    "styles-4-top3-cockpit.css",
    "styles-5-control-v2.css"
  ];
  const styleIndex = await fs.readFile(path.join(root, "public", "styles.css"), "utf8");
  let lastImportPosition = -1;
  for (const sheet of styleSheets) {
    const importPosition = styleIndex.indexOf(`@import url("/${sheet}")`);
    assert(importPosition >= 0, `styles.css should import ${sheet}.`);
    assert(importPosition > lastImportPosition, `styles.css must keep ${sheet} in its original cascade position.`);
    lastImportPosition = importPosition;
  }
  const styleParts = await Promise.all(styleSheets.map(sheet => fs.readFile(path.join(root, "public", sheet), "utf8")));
  const styleSource = styleParts.join("\n");
  assert(styleSource.includes(".race-strip .laps-box.flag-yellow") && styleSource.includes("#ffe56a"), "Yellow flag should visibly recolor the laps-to-go panel.");
  assert(styleSource.includes(".race-strip .laps-box.flag-red") && styleSource.includes("#ff6b66"), "Red flag should visibly recolor the laps-to-go panel.");
  assert(styleSource.includes(".race-strip .laps-box.flag-final") && styleSource.includes("20px 20px"), "Final/checkered flag should visibly checker the laps-to-go panel.");
}

async function request(baseUrl, endpoint, options = {}) {
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), options.timeoutMs || 5000);
  try {
    const response = await fetch(`${baseUrl}${endpoint}`, {
      method: options.body ? "POST" : "GET",
      signal: controller.signal,
      headers: options.body ? { "content-type": "application/json" } : undefined,
      body: options.body ? JSON.stringify(options.body) : undefined
    });
    const text = await response.text();
    let body = text;
    try {
      body = text ? JSON.parse(text) : null;
    } catch (_err) {}
    return { response, body };
  } finally {
    clearTimeout(timeout);
  }
}

async function checkEventStream(baseUrl, endpoint) {
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), 5000);
  try {
    const response = await fetch(`${baseUrl}${endpoint}`, {
      signal: controller.signal,
      headers: { accept: "text/event-stream" }
    });
    assert(response.ok, `${endpoint} should open successfully.`);
    assert((response.headers.get("content-type") || "").includes("text/event-stream"), `${endpoint} should be an event stream.`);
    const reader = response.body?.getReader();
    const chunk = await reader?.read();
    const text = new TextDecoder().decode(chunk?.value || new Uint8Array());
    assert(text.includes("data:"), `${endpoint} should send initial data.`);
    await reader?.cancel();
  } catch (err) {
    fail(`${endpoint} event stream failed: ${err.message}`);
  } finally {
    clearTimeout(timeout);
  }
}

async function checkLiveServer(baseUrl) {
  const pages = [
    "/control.html",
    "/program.html",
    "/top-banner.html",
    "/side-banner.html",
    "/current-leaders.html",
    "/story-spotlight.html",
    "/weather.html",
    "/ticker.html",
    "/top3-podium.html",
    "/board.html?mode=lineup",
    "/board.html?mode=results"
  ];

  for (const page of pages) {
    const { response, body } = await request(baseUrl, page);
    assert(response.ok, `${page} should load.`);
    assert(String(body || "").trim().length > 100, `${page} should not be empty.`);
  }

  for (const endpoint of ["/events", "/program-events", "/top-banner-events", "/story-output-events"]) {
    await checkEventStream(baseUrl, endpoint);
  }

  await request(baseUrl, "/api/reset", { body: {} });
  // Driver names here are deliberately too short to pass isUsableProfileName so a
  // live check never teaches fake drivers into the real driver database.
  const sample = `$RMDTL,""
$B,1,"LRW Deep Check Feature"
$C,1,"LRW Deep Check"
$COMP,"101","991",1,"ZZ","1","NC"," "
$COMP,"102","992",1,"ZZ","2","VA"," "
$COMP,"103","993",1,"ZZ","3","IL"," "
$G,1,"101",12,"00:03:05.100"
$G,2,"102",12,"00:03:06.450"
$G,3,"103",11,"00:03:07.200"
$J,"101","00:00:15.220","00:03:05.100"
$J,"102","00:00:15.310","00:03:06.450"
$J,"103","00:00:15.400","00:03:07.200"
$RMS,"race"
$E,"TRACKNAME","Freedom 75"
$F,8,"00:00:00","22:10:12","00:00:00","Green"`;
  const posted = await request(baseUrl, "/api/message", { body: { message: sample } });
  assert(posted.response.ok && posted.body?.ok, "/api/message should accept sample Race Monitor data.");
  assert(posted.body?.state?.runningOrder?.length >= 3, "Sample timing data should produce running order rows.");

  const beforeStyle = await request(baseUrl, "/api/style");
  const originalStyle = beforeStyle.body && typeof beforeStyle.body === "object" ? beforeStyle.body : null;
  const customMetrics = ["lastLap", "bestLap", "gap", "className"];
  const style = await request(baseUrl, "/api/style", {
    body: {
      bannerRaceMode: "racing",
      bannerMetricFields: customMetrics,
      bannerLogoRotateSeconds: 2,
      raceStoryHeldTitle: "Runtime test",
      raceStoryHeldDetail: "Should not ship in style-state",
      raceStoryHeldKey: "deep-check"
    }
  });
  assert(style.response.ok && style.body?.ok, "/api/style should save banner and runtime settings.");
  assert(style.body?.style?.bannerMetricFields?.join(",") === customMetrics.join(","), "Banner metric field selections should round-trip exactly.");
  assert(style.body?.style?.raceStoryHeldTitle === "Runtime test", "Runtime Race Story state should be visible to clients after save.");

  const logoStyle = style.body?.style || {};
  const sponsorCount = Number(logoStyle.bannerSponsorLogoCount || 0);
  if (originalStyle) {
    const restored = await request(baseUrl, "/api/style", { body: originalStyle });
    assert(restored.response.ok && restored.body?.ok, "/api/style should restore original settings after live check.");
  }
  for (let index = 0; index < Math.max(1, sponsorCount); index += 1) {
    const logo = await request(baseUrl, `/banner-logo/sponsor/${index}`);
    assert(logo.response.ok, `/banner-logo/sponsor/${index} should load.`);
  }

  await request(baseUrl, "/api/top-banner-output", { body: { visible: true } });
  await request(baseUrl, "/api/program", { body: { graphic: "topbanner" } });
  for (const graphic of ["leaders", "story", "weather"]) {
    await request(baseUrl, "/api/top-banner-output", { body: { visible: true } });
    const sent = await request(baseUrl, "/api/program", { body: { graphic } });
    assert(sent.response.ok && sent.body?.ok, `${graphic} should be selectable.`);
    const topBanner = await request(baseUrl, "/api/top-banner-output");
    assert(topBanner.body?.visible === true, `${graphic} should not hide the top banner.`);
  }

  await request(baseUrl, "/api/top-banner-output", { body: { visible: true } });
  await request(baseUrl, "/api/program", { body: { graphic: "results" } });
  const afterResults = await request(baseUrl, "/api/top-banner-output");
  assert(afterResults.body?.visible === false, "Race Results should hide the top banner.");

  await request(baseUrl, "/api/program", { body: { graphic: "blank" } });
  const blank = await request(baseUrl, "/api/program");
  assert(blank.body?.graphic === "blank", "Clear Output should return program output to blank.");

  // Leave the server clean: drop the injected sample timing data.
  await request(baseUrl, "/api/reset", { body: {} });

  const logoDir = process.env.LIBERTY_LOGO_DIR;
  if (logoDir) {
    const styleFile = await fs.readFile(path.join(logoDir, "style-state.json"), "utf8");
    assert(!/raceStoryHeld/.test(styleFile), "Runtime Race Story fields should not be written to shipped style-state.json.");
  } else {
    notes.push("Set LIBERTY_LOGO_DIR while testing if you want style-state runtime contamination checked.");
  }
}

checkRaceMonitorParser();
checkMyRacePassParser();
checkRaceIntelligenceProfiles();
checkRacePhaseDetection();
checkLapsLed();
checkBattleWatch();
await checkLiveMotionHelpers();
await checkPackageSource();

const baseUrl = (process.env.DEEP_CHECK_BASE || process.env.SMOKE_BASE || "").replace(/\/+$/, "");
if (baseUrl) {
  await checkLiveServer(baseUrl);
} else {
  notes.push("Skipped live server checks. Set DEEP_CHECK_BASE=http://127.0.0.1:4000 to include them.");
}

if (failures.length) {
  console.error("Deep check failed:");
  for (const failure of failures) console.error(`- ${failure}`);
  process.exit(1);
}

console.log("Deep check passed.");
for (const note of notes) console.log(note);
