#!/usr/bin/env node
// Replayable on-screen demo for the live-motion graphics pass: position-change
// arrows, FLIP row reordering, fastest-lap purple, podium accents.
//
// Usage:
//   node scripts/motion-demo.mjs                  (against http://127.0.0.1:4000)
//   DEMO_BASE=http://127.0.0.1:4100 node scripts/motion-demo.mjs
//
// Injects fake timing through /api/message (the same path deep-check uses).
// Drivers carry realistic names so the graphics read like a real broadcast.
// That is safe for the driver database: profiles are keyed class|number and
// the demo class ("LRW Motion Demo") never matches a real field, and one demo
// run leaves seenCount at 1 - below every fallback threshold. Leaves the demo
// data on screen when done; use Start New Event to clear it.

const base = (process.env.DEMO_BASE || "http://127.0.0.1:4000").replace(/\/+$/, "");

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function post(endpoint, body) {
  const response = await fetch(`${base}${endpoint}`, {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify(body)
  });
  if (!response.ok) throw new Error(`${endpoint} failed (${response.status})`);
  return response.json();
}

function message(lines) {
  return post("/api/message", { message: lines.join("\n") });
}

const roster = [
  { id: "101", num: "24", first: "Ricky", last: "Dalton", home: "Mooresville, NC" },
  { id: "102", num: "42", first: "Marcus", last: "Vance", home: "Knoxville, TN" },
  { id: "103", num: "7", first: "Cody", last: "Brockman", home: "Eldora, OH" },
  { id: "104", num: "88", first: "Tyler", last: "Hutchins", home: "Pevely, MO" },
  { id: "105", num: "15", first: "Dale", last: "Whitfield", home: "Sharon, PA" },
  { id: "106", num: "31", first: "Austin", last: "Creech", home: "Volusia, FL" },
  { id: "107", num: "55", first: "Brett", last: "Salazar", home: "Fairbury, IL" },
  { id: "108", num: "9", first: "Jimmy", last: "Okafor", home: "Wheatland, MO" },
  { id: "109", num: "3", first: "Logan", last: "Pruett", home: "Brandon, SD" },
  { id: "110", num: "21X", first: "Shane", last: "McAllister", home: "Batesville, AR" },
  { id: "111", num: "47", first: "Colt", last: "Devers", home: "Tulsa, OK" },
  { id: "112", num: "12", first: "Wes", last: "Tanaka", home: "Bakersfield, CA" },
  { id: "113", num: "78", first: "Bobby", last: "Lindqvist", home: "Granite City, IL" },
  { id: "114", num: "5M", first: "Hank", last: "Marbury", home: "Selinsgrove, PA" },
  { id: "115", num: "92", first: "Eli", last: "Vasquez", home: "Devils Lake, ND" },
  { id: "116", num: "01", first: "Denny", last: "Polk", home: "Cedar Lake, IN" }
];

function compLines() {
  return roster.map(car => `$COMP,"${car.id}","${car.num}",1,"${car.first}","${car.last}","${car.home}"," "`);
}

function gridLines(order, laps) {
  // Ticks name the cars that moved; everyone else falls in behind in roster
  // order, so the full 16-car field always has positions (and the boards have
  // enough rows to scroll).
  const fullOrder = [...order, ...roster.map(car => car.id).filter(id => !order.includes(id))];
  return fullOrder.map((id, index) => `$G,${index + 1},"${id}",${laps - (index >= 6 ? 1 : 0)},"00:0${3 + index}:0${index}.${100 + index * 70}"`);
}

console.log(`Liberty Raceworks motion demo against ${base} (~60 seconds, every feature)`);
console.log("Watch the top banner, side tower, or a running/results board.");

// A live timing connection would fight the injected data - drop it first.
await post("/api/race-monitor/disconnect", {}).catch(() => {});
await post("/api/myracepass/disconnect", {}).catch(() => {});
await post("/api/reset", {});

await message([
  '$RMDTL,""',
  '$B,1,"LRW Motion Demo Feature"',
  '$C,1,"LRW Motion Demo"',
  ...compLines(),
  ...gridLines(["101", "102", "103", "104", "105", "106", "107", "108"], 10),
  '$H,1,"101",6,"00:00:15.112"',
  '$J,"101","00:00:15.220","00:02:35.100"',
  '$RMS,"race"',
  '$E,"TRACKNAME","Freedom 75"',
  '$F,15,"00:00:00","20:10:00","00:00:00","Yellow"'
]);
console.log(" 0s  Field loads under yellow: entrance stagger, caution accent wash.");
await sleep(5000);

await message(['$F,15,"00:00:00","20:10:05","00:00:00","Green"']);
console.log(" 5s  GREEN GREEN GREEN: green chip + light sweep across banner and tower.");
await sleep(6000);

await message([
  ...gridLines(["101", "102", "103", "104", "107", "105", "106", "108"], 11),
  '$H,1,"102",11,"00:00:14.987"',
  '$J,"102","00:00:14.987","00:02:50.700"',
  '$F,14,"00:00:00","20:10:11","00:00:00","Green"'
]);
console.log("11s  FASTEST LAP: #42 goes purple (row sweep + chip); #55 gains two (arrows).");
await sleep(8000);

await message([
  ...gridLines(["102", "101", "103", "104", "107", "105", "106", "108"], 12),
  '$F,13,"00:00:00","20:10:19","00:00:00","Green"'
]);
console.log("19s  NEW LEADER: #42 takes P1 - green chip, rows slide, gold leader plate moves.");
await sleep(7000);

// Two cars nose-to-tail across several ticks wakes the battle watch.
for (let tick = 0; tick < 4; tick += 1) {
  await message([
    `$G,1,"102",${13 + tick},"00:0${Math.floor((250 + tick * 15) / 60)}:${String((250 + tick * 15) % 60).padStart(2, "0")}.100"`,
    `$G,2,"103",${13 + tick},"00:0${Math.floor((250 + tick * 15) / 60)}:${String((250 + tick * 15) % 60).padStart(2, "0")}.${480 + tick * 10}"`,
    `$F,${12 - tick},"00:00:00","20:10:${String(26 + tick).padStart(2, "0")}","00:00:00","Green"`
  ]);
  await sleep(1200);
}
console.log("26s  BATTLE: #42 vs #7 under half a second - amber chip + both rows edged amber.");
await sleep(7000);

await message([
  ...gridLines(["102", "107", "103", "101", "104", "105", "106", "108"], 17),
  '$F,8,"00:00:00","20:10:38","00:00:00","Green"'
]);
console.log("38s  BIGGEST MOVER: #55 up to P2 - battle releases, blue story chip follows.");
await sleep(9000);

await message([
  ...gridLines(["102", "107", "103", "101", "104", "105", "106", "108"], 18),
  '$F,1,"00:00:00","20:10:47","00:00:00","White"'
]);
console.log("47s  WHITE FLAG: white laps plate + WHITE FLAG - FINAL LAP chip.");
await sleep(7000);

await message([
  ...gridLines(["102", "107", "103", "101", "104", "105", "106", "108"], 19),
  '$F,0,"00:00:00","20:10:54","00:00:00","Finished"'
]);
console.log("54s  CHECKERED: gold sweep, checker plate, #42 WINS chip, winner row gold.");
console.log("Done. Send Results or Top 3 Podium to see the gold/silver/bronze plates.");
console.log("(Logos crossfade on their own rotation; glass shows over video or /glass-preview.html.)");
