/**
 * A road is a road on every board, or it is a symbol on one of them.
 *
 * Measured on these packs: San Francisco's roads are 19–32 m wide and Southern
 * California's 31–55 m, which is what those roads are. California's US-101 and
 * I-5 are **1,919 m and 2,034 m** — wider than the cities they join, and the
 * most prominent marks on the state board. At the seam one has to become the
 * other, and a 2 km ribbon turning into a 30 m line is a change of drawing
 * convention rather than an LOD transition.
 *
 * Rule (b) reads every width as true metres and floors it at the narrowest
 * fraction of a board span the two metro packs already agree on. The floor is
 * the interesting part: it was measured off the shipped packs rather than
 * chosen, and it is what makes "the metro boards do not move" a property of the
 * rule rather than a coincidence to be checked.
 */

import assert from "node:assert/strict";
import { afterEach, describe, it } from "node:test";
import CALIFORNIA from "../cities/california.ts";
import { LEGIBLE_SPAN_FRACTION, reconciledRoadWidth, setReconcile } from "../cities/reconcile.ts";
import SF from "../cities/sf.ts";
import SOCAL from "../cities/socal.ts";
import type { City } from "../engine/types.ts";
import { World } from "../engine/world.ts";

const metresPerUnit = (city: City): number => 111_320 / city.latScale;

/** Every road on a board, in true metres, as the engine will draw it. */
function drawn(city: City): number[] {
  const world = new World(city);
  return world.city.roads.map((road) => road.width * world.metresPerUnit);
}

afterEach(() => setReconcile(null));

describe("road width", () => {
  it("is a symbol two kilometres wide on the state board today", () => {
    setReconcile(false);
    const widths = drawn(CALIFORNIA);
    assert.deepEqual(widths.map(Math.round), [1919, 2034]);
  });

  it("leaves both metro boards untouched, to the digit", () => {
    setReconcile(false);
    const before = { sf: drawn(SF), socal: drawn(SOCAL) };
    setReconcile(["roads"]);
    assert.deepEqual(drawn(SF), before.sf);
    assert.deepEqual(drawn(SOCAL), before.socal);
  });

  /**
   * Why the floor is 0.000199 and not a round number: the two metro packs,
   * authored separately, put their narrowest road at 0.00019940 and 0.00020356
   * of their own span. Two authors agreeing to within 2% about how thin a line
   * may get is the measurement the constant is taken from, and the floor sits
   * just under the tighter of the two so that neither board moves.
   */
  it("sits just under the narrowest road either metro pack authored", () => {
    // "As authored" is the whole subject, and `span` is derived from
    // `world.lngScale` — which `projection` is now on by default to change. Pin
    // the flag off so this measures the pack rather than the product default.
    setReconcile(false);
    for (const pack of [SF, SOCAL]) {
      const world = new World(pack);
      const span = Math.max(
        (pack.bounds.maxLat - pack.bounds.minLat) * pack.latScale,
        (pack.bounds.maxLng - pack.bounds.minLng) * world.lngScale,
      );
      const narrowest = Math.min(...pack.roads.map((road) => road.width)) / span;
      assert.ok(narrowest > LEGIBLE_SPAN_FRACTION, `${pack.id}: ${narrowest}`);
      assert.ok(narrowest < LEGIBLE_SPAN_FRACTION * 1.05, `${pack.id}: ${narrowest}`);
    }
  });

  it("collapses California's freeways by 9x, onto the floor", () => {
    setReconcile(["roads"]);
    const widths = drawn(CALIFORNIA);
    for (const metres of widths) {
      assert.ok(metres > 200 && metres < 220, `${metres} m`);
    }
    assert.ok(1919 / (widths[0] as number) > 9);
  });

  /**
   * The floor is a floor, not a width: a road wider in true metres than the
   * floor keeps its own width. Nothing on the shipped boards exercises this —
   * every metro road clears the floor and both California roads fall onto it —
   * so it is asserted directly rather than left to be discovered by a pack that
   * one day authors a 400 m causeway on a small board.
   */
  it("is a floor and not a width", () => {
    const wide = { ...CALIFORNIA.roads[0], widthM: 400_000 } as (typeof CALIFORNIA.roads)[number];
    const units = reconciledRoadWidth(CALIFORNIA, wide);
    assert.ok(Math.abs(units * metresPerUnit(CALIFORNIA) - 400_000) < 1);
  });

  it("reads an undeclared width as true metres already", () => {
    const street = SF.roads.find((road) => road.kind === "street");
    assert.ok(street !== undefined && street.widthM === undefined);
    assert.ok(Math.abs(reconciledRoadWidth(SF, street) - street.width) < 1e-9);
  });
});
