/**
 * The freeway corridor on the statewide board, which had two problems and only
 * one of them was visible.
 *
 * **It read as a wireframe.** At 1,919 m to the scene unit the whole corridor is
 * about eleven pixels wide from the default camera, and eleven pixels of flat
 * mid-grey lying exactly on the ground is a line somebody drew on a map. It is
 * now a graded crown with two batters, which gives it three value bands and a
 * normal that is not straight up — see `createFreewayWorld`.
 *
 * **It cost a fifth of the board's triangle budget on things nobody can see.**
 * Guardrails and median walls were tubes at two segments per draped sample on a
 * corridor already sampled every kilometre, and the reflectors were 2,296 boxes
 * eighteen millimetres across. Between them: 110,000 triangles on a board with
 * 75,000 to spare, which is why the state had no mountains and no cities on it.
 *
 * The two assertions below are the ones that would have caught the two defects
 * this cost a rebuild to find:
 *
 *   - **Every batter faces the sky.** `deck` materials are `DoubleSide` and
 *     three.js negates the shading normal on a back face, so a strip whose two
 *     rails were emitted in the opposite order to its neighbours renders as an
 *     unlit black band. One did, the length of US-101, and it typechecked.
 *   - **The corridor stays under its triangle ceiling.** The real gate is
 *     `scripts/performance-budget.mjs`, which needs a browser and a minute; this
 *     runs in milliseconds and fails on the line that caused the regression.
 */

import assert from "node:assert/strict";
import { describe, it } from "node:test";
import * as THREE from "three";

import CALIFORNIA_CITY from "../../cities/california.ts";
import { createFreewayWorld } from "../../engine/structures.ts";
import type { World } from "../../engine/world.ts";
import CALIFORNIA_TRANSPORT from "../../transport/california.ts";

/** Flat ground and a linear projection: the corridor's own shape, nothing else. */
const flatWorld = {
  city: CALIFORNIA_CITY,
  project(lat: number, lng: number): [number, number] {
    return [(lng + 121) * 47, -(lat - 36) * 58];
  },
  groundAt(): number {
    return 0;
  },
} as unknown as World;

function meshesIn(group: THREE.Object3D): THREE.Mesh[] {
  const found: THREE.Mesh[] = [];
  group.traverse((object) => {
    if (object instanceof THREE.Mesh) found.push(object);
  });
  return found;
}

function triangles(mesh: THREE.Mesh): number {
  const geometry = mesh.geometry;
  const index = geometry.getIndex();
  const per = index ? index.count / 3 : geometry.getAttribute("position").count / 3;
  return per * (mesh instanceof THREE.InstancedMesh ? mesh.count : 1);
}

describe("California corridor", () => {
  const group = createFreewayWorld(flatWorld, CALIFORNIA_TRANSPORT);
  const all = meshesIn(group);

  it("gives the earthwork a crown and two batters that both face the sky", () => {
    const embankment = all.find((mesh) => mesh.name === "freeway:embankment");
    assert.ok(embankment, "the corridor has no embankment; it is a flat ribbon again");

    const normals = embankment.geometry.getAttribute("normal");
    assert.ok(normals, "the embankment lost the normals mergeGeometries matches on");
    let tilted = 0;
    for (let index = 0; index < normals.count; index += 1) {
      const y = normals.getY(index);
      assert.ok(y > 0, `embankment normal ${index} points into the ground (y=${y.toFixed(3)})`);
      if (y < 0.999) tilted += 1;
    }
    // And it is a batter, not another flat deck: an untilted strip would pass
    // the test above and still be the thing this replaced.
    assert.ok(tilted > normals.count * 0.9, "the embankment is flat; it will not catch the sun");

    // All four spans — two carriageside batters on each of two corridors —
    // merged into the one mesh. A dropped bucket looks like an efficient one.
    assert.ok(
      normals.count > 2_000,
      `the embankment merged to only ${normals.count} vertices`,
    );
  });

  it("keeps the whole corridor inside the triangle share it was budgeted", () => {
    const total = all.reduce((sum, mesh) => sum + triangles(mesh), 0);
    // 142,000 before the reclaim, on a board with a 750,000 cap that was already
    // measuring 675,000. 95,000 is comfortably above what it emits and low
    // enough to fail if anyone doubles a tube's tessellation again.
    assert.ok(total < 95_000, `the corridor is ${Math.round(total)} triangles`);
    // A floor as well, because the cheapest way to pass the line above is to
    // stop drawing the corridor.
    assert.ok(total > 50_000, `the corridor is only ${Math.round(total)} triangles`);
  });
});
