/** Determinism and lifecycle tests for statewide road traffic. */

import assert from "node:assert/strict";
import { describe, it } from "node:test";
import CALIFORNIA_TRANSPORT from "../transport/california.ts";
import {
  VehicleSimulation,
  buildRoutePath,
  distanceMetres,
  sampleRoute,
} from "../transport/vehicleSim.ts";

describe("vehicle simulation", () => {
  it("builds both complete routes and samples their declared endpoints", () => {
    for (const id of ["la-sf-us-101", "la-sf-i-5"]) {
      const path = buildRoutePath(CALIFORNIA_TRANSPORT, id);
      assert.ok(path.lengthM > 500_000);
      const start = sampleRoute(path, 0);
      const finish = sampleRoute(path, path.lengthM - 0.01);
      assert.ok(distanceMetres(start, { lat: 34.0522, lng: -118.2437 }) < 10);
      assert.ok(distanceMetres(finish, { lat: 37.7749, lng: -122.4194 }) < 20);
    }
  });

  it("is deterministic for the same seed, route, and frame sequence", () => {
    const a = new VehicleSimulation(CALIFORNIA_TRANSPORT, {
      routeId: "la-sf-us-101",
      count: 8,
      seed: 42,
    });
    const b = new VehicleSimulation(CALIFORNIA_TRANSPORT, {
      routeId: "la-sf-us-101",
      count: 8,
      seed: 42,
    });
    for (const dt of [0.016, 0.033, 0.2, 0.041, 0.08]) {
      a.tick(dt);
      b.tick(dt);
    }
    assert.deepEqual(a.poses(), b.poses());
  });

  it("caps a background-tab delta and keeps every pose finite", () => {
    const sim = new VehicleSimulation(CALIFORNIA_TRANSPORT, {
      routeId: "la-sf-i-5",
      count: 16,
    });
    const path = buildRoutePath(CALIFORNIA_TRANSPORT, "la-sf-i-5");
    const before = sim.poses()[0]?.distanceM ?? 0;
    sim.tick(600);
    const after = sim.poses()[0]?.distanceM ?? 0;
    const direct = Math.abs(after - before);
    const travelled = Math.min(direct, path.lengthM - direct);
    assert.ok(travelled < 20_000, "a sleeping tab must not replay ten minutes");
    for (const pose of sim.poses()) {
      assert.ok(Number.isFinite(pose.lat));
      assert.ok(Number.isFinite(pose.lng));
      assert.ok(Number.isFinite(pose.headingDeg));
      assert.ok(pose.progress >= 0 && pose.progress < 1);
    }
  });

  it("changes route as one deterministic state transition", () => {
    const sim = new VehicleSimulation(CALIFORNIA_TRANSPORT, {
      routeId: "la-sf-us-101",
      count: 4,
      seed: 7,
    });
    sim.setRoute("la-sf-i-5");
    assert.equal(sim.routeId(), "la-sf-i-5");
    assert.ok(sim.poses().every((pose) => pose.routeId === "la-sf-i-5"));
    assert.equal(sim.poses()[0]?.id, "model-x-hero");
  });

  it("moves reverse traffic south while keeping its heading finite", () => {
    const sim = new VehicleSimulation(CALIFORNIA_TRANSPORT, {
      routeId: "la-sf-us-101",
      count: 4,
      seed: 115,
      timeScale: 1,
    });
    const southbound = sim.poses().find((pose) => pose.direction === -1);
    assert.equal(southbound?.direction, -1);
    const before = southbound?.distanceM ?? 0;
    const beforeLat = southbound?.lat ?? 0;
    sim.tick(0.1);
    assert.ok((southbound?.distanceM ?? before) < before);
    assert.ok((southbound?.lat ?? beforeLat) < beforeLat);
    assert.ok(Number.isFinite(southbound?.headingDeg));
  });

  it("returns one stable pose view for allocation-free render polling", () => {
    const sim = new VehicleSimulation(CALIFORNIA_TRANSPORT, {
      routeId: "la-sf-us-101",
      count: 4,
    });
    const poses = sim.poses();
    sim.tick(0.1);
    assert.equal(sim.poses(), poses);
    assert.equal(sim.poses()[0], poses[0]);
  });

  it("reports a deterministic same-lane lead vehicle for safety control", () => {
    const sim = new VehicleSimulation(CALIFORNIA_TRANSPORT, {
      routeId: "la-sf-us-101",
      count: 14,
      seed: 115,
    });
    const candidate = sim.poses().slice(1).find((pose) => pose.direction === 1);
    assert.ok(candidate);
    const observation = sim.leadVehicle(candidate.distanceM - 100, 1, candidate.lane);
    assert.ok(observation);
    assert.ok(observation.gapM > 0 && observation.gapM <= 100.000001);
    assert.ok(Number.isFinite(observation.speedMps));
    assert.deepEqual(sim.leadVehicle(candidate.distanceM - 100, 1, candidate.lane), observation);
  });
});
