/**
 * The wiring seams, tested where they can actually be exercised.
 *
 * `createOfficeScene` is the one scene in this repo that can be built under
 * `node --test`: it needs a DOM element and a `window` for `OrbitControls` and
 * `matchMedia`, and nothing else — no WebGL context, no Worker, no heightfield.
 * So the office half of the integration surface is tested for real, against the
 * shipped packs, rather than by reading the source and hoping.
 *
 * The city half is not, and the asymmetry is honest rather than lazy:
 * `createScene` awaits a terrain Worker and takes a live `Stage`, so the only
 * place it can be exercised is a browser. `scripts/ui-smoke.mjs` is where that
 * happens, and the two source-level assertions at the bottom of this file are
 * the belt to that brace — they fail if the seam is *deleted*, which is the
 * failure a browser test is slowest to tell you about.
 *
 * The renderer is a fake of the same specific kind `src/test/render` uses:
 * `PMREMGenerator` never touches WebGL directly, so stubbing `render` and
 * `setRenderTarget` runs the real generator, the real target allocation and the
 * real blur chain and leaves only the pixels unwritten.
 */

import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import test from "node:test";
import * as THREE from "three";

const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../..");

// ---- The environment the browser supplies and Node does not ----------------
//
// Installed before the modules are imported, because `scenekit.ts` reads
// `window.matchMedia` at construction and `officeScene.ts` reaches it through a
// static import chain. The dynamic imports below are what let this run first.

(globalThis as unknown as { window: unknown }).window = {
  matchMedia: () => ({ matches: false, addEventListener() {}, removeEventListener() {} }),
  innerWidth: 1_200,
  innerHeight: 800,
  devicePixelRatio: 1,
  addEventListener() {},
  removeEventListener() {},
};

const { createOfficeScene } = await import("../../interiors/officeScene.ts");
const { createEnvironmentRig } = await import("../../engine/environmentRig.ts");
const LUMBRIDGE_HQ = (await import("../../offices/lumbridge-hq.ts")).default;
const MATEO_COURT = (await import("../../offices/mateo-court.ts")).default;
const { initialDeviceState } = await import("../../devices/types.ts");
const { createSimulatedVehicleTelemetry } = await import("../../transport/vehicleTelemetry.ts");
const { arrivalGroundFor } = await import("../../transport/exteriorVehicle.ts");

type Office = typeof LUMBRIDGE_HQ;

const PACKS: readonly (readonly [string, Office])[] = [
  ["lumbridge-hq", LUMBRIDGE_HQ],
  ["mateo-court", MATEO_COURT],
];

// ---- Fakes -----------------------------------------------------------------

/** Everything `OrbitControls` and `SceneKit` touch on the canvas, and no more. */
function fakeDom(): HTMLElement {
  return {
    style: {},
    clientWidth: 1_200,
    clientHeight: 800,
    addEventListener() {},
    removeEventListener() {},
    setPointerCapture() {},
    releasePointerCapture() {},
    getBoundingClientRect: () => ({
      left: 0, top: 0, width: 1_200, height: 800, right: 1_200, bottom: 800, x: 0, y: 0,
    }),
    getRootNode: () => ({ addEventListener() {}, removeEventListener() {} }),
    ownerDocument: { addEventListener() {}, removeEventListener() {} },
  } as unknown as HTMLElement;
}

interface FakeRenderer {
  renders: number;
  targets: Set<THREE.WebGLRenderTarget>;
  as(): THREE.WebGLRenderer;
}

/** A renderer that allocates render targets and never draws to them. */
function fakeRenderer(): FakeRenderer {
  const targets = new Set<THREE.WebGLRenderTarget>();
  const state: FakeRenderer = {
    renders: 0,
    targets,
    as: () => stub as unknown as THREE.WebGLRenderer,
  };
  const stub = {
    autoClear: true,
    toneMapping: THREE.NoToneMapping,
    xr: { enabled: false },
    state: { buffers: { depth: { getReversed: () => false } } },
    getRenderTarget: () => null,
    getActiveCubeFace: () => 0,
    getActiveMipmapLevel: () => 0,
    getClearColor: (target: THREE.Color) => target,
    getClearAlpha: () => 1,
    setClearColor: () => {},
    setClearAlpha: () => {},
    clearDepth: () => {},
    compile: () => {},
    setRenderTarget(target: THREE.WebGLRenderTarget | null) {
      if (target) targets.add(target);
    },
    render() {
      state.renders += 1;
    },
  };
  return state;
}

/** Two aeroplanes over the studio, at bearings this test can predict. */
function fakeFlights(site: NonNullable<Office["site"]>) {
  let polls = 0;
  return {
    interval: 1,
    polls: () => polls,
    poll() {
      polls += 1;
      return [
        // Due north of the site and high: comfortably above the elevation floor.
        { id: "north", lat: site.lat + 0.05, lng: site.lng, altitude: 6_000, heading: 180 },
        // Due east and equally high.
        { id: "east", lat: site.lat, lng: site.lng + 0.05, altitude: 6_000, heading: 270 },
        // On the deck a long way off: below the floor, and must not be drawn.
        { id: "below", lat: site.lat + 2, lng: site.lng, altitude: 100, heading: 0 },
      ];
    },
  };
}

function build(pack: Office, extra: Record<string, unknown> = {}) {
  const renderer = fakeRenderer();
  const rig = createEnvironmentRig(renderer.as());
  const scene = createOfficeScene(pack, {
    dom: fakeDom(),
    depth: "public",
    // `low` is flat Lambert with no textures: it builds the same graph and the
    // same materials-per-role decisions, in a fraction of the canvas work.
    quality: "low",
    environment: rig,
    exteriorVehicle: { detail: "corridor", seed: 7 },
    ...(pack.site ? { horizon: { drop: pack.site.elevation } } : {}),
    ...extra,
  });
  return { scene, rig, renderer };
}

function named(root: THREE.Object3D, name: string): THREE.Object3D | null {
  return root.children.find((child) => child.name === name) ?? null;
}

// ---- The environment rig ---------------------------------------------------

for (const [id, pack] of PACKS) {
  test(`${id}: createOfficeScene puts a real environment texture on the scene`, () => {
    const { scene, rig, renderer } = build(pack);
    try {
      assert.ok(
        scene.scene.environment instanceof THREE.Texture,
        "the office must have an environment before its first frame, or every metal " +
          "role in the room renders as grey plastic",
      );
      assert.equal(scene.scene.environmentIntensity, 1);
      assert.ok(renderer.renders > 0, "the PMREM blur chain never ran");
    } finally {
      scene.dispose();
      rig.dispose();
    }
  });

  test(`${id}: disposing the scene releases it from the rig's ledger`, () => {
    const { scene, rig } = build(pack);
    try {
      assert.ok(scene.scene.environment !== null);
      scene.dispose();
      assert.equal(
        scene.scene.environment,
        null,
        "a disposed scene left holding an environment is a disposed scene the rig " +
          "still has a strong reference to — one whole floor plate leaked per switch",
      );
    } finally {
      rig.dispose();
    }
  });
}

test("a rebuilt environment does not reach a scene that has been released", () => {
  const { scene, rig } = build(LUMBRIDGE_HQ);
  const second = createOfficeScene(MATEO_COURT, {
    dom: fakeDom(), depth: "public", quality: "low", environment: rig,
  });
  try {
    scene.dispose();
    // A different sun, so the rig's coarse fingerprint moves and it rebuilds.
    second.setLighting({
      sun: { direction: [-0.7, 0.2, 0.1], color: 0xffb070, intensity: 0.6 },
      hemisphere: { sky: 0x30435c, ground: 0x201a14, intensity: 0.5 },
      ambient: { color: 0xffffff, intensity: 0.1 },
      sky: null,
      fog: null,
    });
    assert.equal(scene.scene.environment, null, "the released scene was written to again");
    assert.ok(second.scene.environment instanceof THREE.Texture);
  } finally {
    second.dispose();
    rig.dispose();
  }
});

// ---- The device layer ------------------------------------------------------

for (const [id, pack] of PACKS) {
  test(`${id}: the authored hardware is mounted and offered to the panel`, () => {
    const { scene, rig } = build(pack);
    try {
      const declared = pack.levels.flatMap((level) => level.floorplan.devices ?? []);
      assert.ok(declared.length > 0, `${id} authors no devices, so this test proves nothing`);
      assert.deepEqual(
        [...scene.devices].map((d) => d.id).sort(),
        [...declared].map((d) => d.id).sort(),
        "every declaration this pack authored should resolve against its own plan",
      );
      const layer = named(scene.scene, "devices");
      assert.ok(layer !== null, "no device layer in the scene graph");
      assert.equal(
        layer.children.length,
        declared.length,
        "one mount per resolved declaration",
      );
    } finally {
      scene.dispose();
      rig.dispose();
    }
  });
}

test("a device reading reaches the indicator on the hardware", () => {
  const { scene, rig } = build(LUMBRIDGE_HQ);
  try {
    const declaration = scene.devices[0];
    assert.ok(declaration, "lumbridge-hq authors at least one device");
    const indicators = (): THREE.Material[] => {
      const found: THREE.Material[] = [];
      named(scene.scene, "devices")?.traverse((object) => {
        if (object.name === "indicator") {
          object.traverse((child) => {
            const mesh = child as THREE.Mesh;
            if (mesh.isMesh && !Array.isArray(mesh.material)) found.push(mesh.material);
          });
        }
      });
      return found;
    };

    scene.setDeviceStates([{ ...initialDeviceState(declaration, 0), powered: false }]);
    const off = indicators().map((m) => (m as THREE.MeshStandardMaterial).color.getHex());
    scene.setDeviceStates([{ ...initialDeviceState(declaration, 0), powered: true, muted: false }]);
    const on = indicators().map((m) => (m as THREE.MeshStandardMaterial).color.getHex());

    assert.ok(off.length > 0, "the device assets carry no indicator meshes");
    assert.notDeepEqual(on, off, "powering a device changed nothing anybody could see");
  } finally {
    scene.dispose();
    rig.dispose();
  }
});

// ---- The exterior ----------------------------------------------------------

for (const [id, pack] of PACKS) {
  test(`${id}: the exterior matches the ground its site actually has`, () => {
    const arrival = pack.site?.arrival;
    assert.ok(arrival, `${id} authors no arrival anchor`);
    const { scene, rig } = build(pack);
    try {
      const exterior = named(scene.scene, "office-exterior");
      assert.ok(exterior !== null, "no exterior in the scene graph");
      assert.equal(exterior.userData.arrivalKind, "vehicle-stall");

      exterior.updateMatrixWorld(true);
      let car: THREE.Object3D | null = null;
      exterior.traverse((object) => {
        if (object.userData.vehicleModel === "model-x") car = object;
      });

      // An apron is ground, and `lumbridge-hq`'s studio floor is 188 m above the
      // pavement outside it. Built literally at the stall's own storey it is a
      // paved pad and a car in open sky beside the west wall, which is what this
      // shipped for one build. Read off the site rather than the id, because
      // reading off the site is exactly what the layer under test must do.
      if (arrivalGroundFor(pack.site?.elevation ?? 0) === "air") {
        assert.equal(car, null, `${id} parked a car in the air`);
        assert.equal(exterior.children.length, 0, "an off-ground site draws nothing");
        return;
      }

      assert.ok(car !== null, "the apron was built without a car on it");
      const at = (car as THREE.Object3D).getWorldPosition(new THREE.Vector3());
      const floorY = scene.plan.level(arrival.levelId)?.floorY ?? 0;
      assert.ok(
        Math.hypot(at.x - arrival.position.x, at.z - arrival.position.z) <= 0.5,
        `car at ${at.x.toFixed(2)}, ${at.z.toFixed(2)} against stall ` +
          `${arrival.position.x}, ${arrival.position.z}`,
      );
      assert.ok(
        Math.abs(at.y - floorY) <= 1.5,
        `the apron must stand on the floor of ${arrival.levelId} (${floorY} m), not at ` +
          `the plan origin — the stall names a storey for a reason`,
      );
    } finally {
      scene.dispose();
      rig.dispose();
    }
  });
}

test("vehicle telemetry can be pushed every frame without moving anything twice", () => {
  const { scene, rig } = build(LUMBRIDGE_HQ);
  const source = createSimulatedVehicleTelemetry({
    seed: 3, fixedStepSeconds: 0.5, ambientC: 19,
  });
  try {
    // Idempotent by contract: `apply` is signature-guarded, so the frames that
    // spend no simulated step must cost a comparison and change nothing.
    for (let i = 0; i < 20; i += 1) scene.setVehicleTelemetry(source.current());
    source.command({ op: "charge", value: true });
    for (let i = 0; i < 40; i += 1) source.stepFixed();
    scene.setVehicleTelemetry(source.current());
    assert.ok(source.current().pluggedIn, "the command never reached the simulator");
  } finally {
    scene.dispose();
    rig.dispose();
  }
});

// ---- Overhead traffic ------------------------------------------------------

test("the studio sky carries the same aircraft the board outside is drawing", async () => {
  const site = MATEO_COURT.site;
  assert.ok(site, "mateo-court is sited");
  const flights = fakeFlights(site);
  const { scene, rig } = build(MATEO_COURT, { flights });
  try {
    const layer = named(scene.scene, "overhead-traffic");
    assert.ok(layer !== null, "a sited office with a flight source drew no sky traffic");
    const mesh = layer.children[0] as THREE.InstancedMesh;
    assert.ok(mesh.isInstancedMesh, "overhead traffic must be one draw call, not one per track");
    assert.equal(mesh.count, 0, "nothing is drawn before the first poll lands");

    scene.tick(2, 2);
    // The poll is a promise even for a synchronous source, so let the microtask
    // queue drain before reading what it placed.
    await Promise.resolve();
    await Promise.resolve();

    assert.equal(flights.polls(), 1, "the office must poll the source it was handed");
    assert.equal(
      mesh.count,
      2,
      "the aeroplane below the elevation floor was drawn through the ground",
    );

    const centre = scene.plan.bounds.center;
    const matrix = new THREE.Matrix4();
    const north = new THREE.Vector3();
    mesh.getMatrixAt(0, matrix);
    north.setFromMatrixPosition(matrix);
    const east = new THREE.Vector3();
    mesh.getMatrixAt(1, matrix);
    east.setFromMatrixPosition(matrix);

    /*
     * The building's own frame: `site.heading` is the bearing its −Z points
     * along, so an aeroplane due north of a north-facing pack lands at −Z. Both
     * shipped packs are rotated, so the test asserts the *relationship* rather
     * than a literal axis — the two tracks are ninety degrees apart on the
     * compass and must be ninety degrees apart on the dome.
     */
    const bearingOf = (at: THREE.Vector3) =>
      Math.atan2(at.x - centre.x, -(at.z - centre.z)) * 180 / Math.PI;
    const separation = Math.abs(((bearingOf(east) - bearingOf(north) + 540) % 360) - 180);
    assert.ok(
      Math.abs(separation - 90) < 2,
      `two tracks 90° apart on the compass came out ${separation.toFixed(1)}° apart on the dome`,
    );
    assert.ok(north.y > 0, "an aeroplane above the horizon must be above the floor plate");
  } finally {
    scene.dispose();
    rig.dispose();
  }
});

test("an office with no site draws no sky traffic and asks the feed nothing", () => {
  const site = LUMBRIDGE_HQ.site;
  assert.ok(site);
  const flights = fakeFlights(site);
  const { site: _site, ...unsited } = LUMBRIDGE_HQ;
  const renderer = fakeRenderer();
  const rig = createEnvironmentRig(renderer.as());
  const scene = createOfficeScene(unsited as typeof LUMBRIDGE_HQ, {
    dom: fakeDom(), depth: "public", quality: "low", environment: rig, flights,
  });
  try {
    assert.equal(named(scene.scene, "overhead-traffic"), null);
    scene.tick(2, 2);
    assert.equal(
      flights.polls(),
      0,
      "a pack with no coordinate has no bearing to put an aeroplane on, so it must " +
        "not be spending a request to find out where one is",
    );
  } finally {
    scene.dispose();
    rig.dispose();
  }
});

// ---- The clickable aircraft ------------------------------------------------

const { createFlightLayer, aircraftDetail } = await import("../../engine/flights.ts");

/**
 * Enough of a `World` for the flight layer: a projection and a vertical scale.
 *
 * The real one owns a 0.53M-point heightfield and two and a half seconds of
 * build, none of which this layer touches — it projects a coordinate and scales
 * an altitude, and that is the whole of its contact with the world.
 */
const flatWorld = {
  project: (lat: number, lng: number) => [lng * 100, -lat * 100],
  metres: (m: number) => m / 100,
  metresPerUnit: 100,
} as unknown as Parameters<typeof createFlightLayer>[0];

test("every aeroplane on the board is a pick target that knows which one it is", () => {
  const layer = createFlightLayer(flatWorld);
  try {
    assert.equal(layer.pickables.length, 0, "nothing to click before the first observation");

    layer.update([
      { id: "a1b2c3", lat: 37.6, lng: -122.4, altitude: 9_000, heading: 310, callsign: "UAL221" },
      { id: "~ddeeff", lat: 37.8, lng: -122.2, altitude: 2_400, heading: 90 },
    ]);
    assert.equal(layer.pickables.length, 2);
    assert.deepEqual(
      layer.pickables.map((object) => object.userData.aircraftId).sort(),
      ["a1b2c3", "~ddeeff"],
      "a raycast hit resolves to an aeroplane through this and nothing else",
    );
    for (const object of layer.pickables) {
      assert.ok(layer.group.children.includes(object), "a pick target that is not drawn");
    }
  } finally {
    layer.dispose();
    assert.equal(layer.pickables.length, 0, "dispose must empty the target list too");
  }
});

test("the card an anonymous visitor gets says what was observed and what was not", () => {
  // The simulator's ids are route names, not transponder addresses, and
  // `aircraftDetail` must decline to present one as the other.
  const invented = aircraftDetail(
    { id: "sfo-departure-2", lat: 37.62, lng: -122.38, altitude: 3_000, heading: 300 },
    { observed: false },
  );
  assert.equal(invented.icao24, null, "a route name is not a Mode S address");
  assert.equal(invented.observed, false);

  const real = aircraftDetail(
    { id: "a1b2c3", lat: 37.62, lng: -122.38, altitude: 3_000, heading: 300, callsign: "UAL221" },
    { observed: true, attribution: ["Data: adsb.lol contributors"] },
  );
  assert.equal(real.icao24, "a1b2c3");
  assert.deepEqual(real.attribution, ["Data: adsb.lol contributors"]);
});

// ---- The daytime sky -------------------------------------------------------

const { createSatelliteLayer } = await import("../../engine/satellites.ts");
const { createStarlinkMeshLayer } = await import("../../engine/starlinkMesh.ts");
const { nightFactor } = await import("../../engine/atmosphere.ts");

/** One satellite high overhead and sunlit, which is the case that clipped white. */
function overheadFix() {
  return {
    noradId: 25_544,
    name: "SMOKE-1",
    group: "starlink" as const,
    azimuth: 0,
    elevation: Math.PI / 3,
    rangeKm: 600,
    // Full sunlight, which is the brightest a dot ever gets and therefore the
    // case that clipped.
    shadow: 0,
    lat: 37.77,
    lng: -122.42,
  };
}

test("the satellite dots are not drawn into a daytime sky", () => {
  const layer = createSatelliteLayer(400);
  try {
    const points = layer.group.children[0] as THREE.Points;
    const alphaAt = () =>
      (points.geometry.getAttribute("color") as THREE.BufferAttribute).getW(0);

    layer.setSkyDarkness(1);
    layer.update([overheadFix()]);
    const night = alphaAt();
    assert.ok(night > 0.1, "a sunlit satellite at 60° must be visible at night");
    assert.ok(layer.group.visible, "the layer must draw at night");

    // `nightFactor` in degrees: the sun at +44°, which is where the California
    // board stood in the screenshot that named this defect.
    layer.setSkyDarkness(nightFactor(44));
    layer.update([overheadFix()]);
    assert.equal(
      alphaAt(),
      0,
      "live defect 1: additive white dots over a sun at +44° clip to hard white " +
        "squares, and they were the first thing anybody saw on this board",
    );
    assert.equal(layer.group.visible, false, "and the draw is skipped entirely");
  } finally {
    layer.dispose();
  }
});

test("a god who turns the sky on at noon still does not get white squares", () => {
  const layer = createSatelliteLayer(400);
  const meshes = createStarlinkMeshLayer({ boardRadius: 400 });
  try {
    for (const target of [layer, meshes]) {
      target.setSkyDarkness(nightFactor(44));
      target.setVisible(true);
      assert.equal(
        target.group.visible,
        false,
        "the hour outranks the switch: both write `group.visible`, and the sky wins",
      );
      target.setSkyDarkness(1);
      assert.equal(target.group.visible, true, "and night gives it straight back");
      target.setVisible(false);
      assert.equal(target.group.visible, false);
    }
  } finally {
    layer.dispose();
    meshes.dispose();
  }
});

test("the water reflects rather than absorbing, now that there is a sky to reflect", async () => {
  // Comments here name the material that was replaced, and correctly; only
  // code counts, exactly as `barrel.test.ts` reasons about DOM globals.
  const source = readFileSync(path.join(ROOT, "src/engine/terrain.ts"), "utf8")
    .replace(/\/\*[\s\S]*?\*\//g, "")
    .replace(/(^|\n)\s*\/\/[^\n]*/g, "$1");
  const water = source.slice(source.indexOf("function createWater"));
  assert.ok(
    !/MeshLambertMaterial/.test(water.slice(0, water.indexOf("\n}"))),
    "Lambert has no specular term at all, which is why half the California board " +
      "rendered as one flat blue value at every hour and from every angle",
  );
  // Assert the PROPERTY, not the formatting. The first draft of this test
  // matched one exact source line — `MeshStandardMaterial({ color: pal.sea,
  // roughness: 0.14, metalness: 0 })` — and the sea that shipped is a
  // multi-line construction at roughness 0.2 with an `onBeforeCompile` patch
  // that flattens it toward the horizon. The implementation was better than the
  // literal and the test failed anyway, which is the failure mode of asserting
  // on source text: it pins the author's first guess rather than the behaviour.
  const build = water.slice(0, water.indexOf("\n}"));
  assert.match(
    build,
    /MeshStandardMaterial\(/,
    "the sea must be a dielectric that reads `scene.environment`",
  );

  const roughness = Number(/roughness:\s*([\d.]+)/.exec(build)?.[1]);
  assert.ok(
    Number.isFinite(roughness) && roughness > 0 && roughness <= 0.35,
    `the sea's roughness is ${roughness}; it must be low enough to return a sun ` +
      `glint and a sky reflection, and above zero so it is water and not a mirror`,
  );
  assert.match(
    build,
    /metalness:\s*0\b/,
    "water is a dielectric: metalness 0 is what makes the reflection white " +
      "rather than tinted by the sea colour",
  );
});

// ---- The seams that only the source can prove ------------------------------

const MAIN = readFileSync(path.join(ROOT, "src/main.ts"), "utf8");

test("a bare URL on one California opens on FiDi, not on the whole state", () => {
  assert.ok(
    MAIN.includes('openingChapter: "fidi"'),
    "the visitor landing lost its city; they will see 1,551 km of tan",
  );
  assert.ok(
    MAIN.includes("CITY_PARAM === null"),
    "the fidi opening must not apply to ?city=california — that is the capture harness",
  );
});

test("main.ts makes exactly one call into the chrome, and writes no visibility itself", () => {
  const applies = [...MAIN.matchAll(/chrome\?\.apply\(chromeState\(/g)].length;
  assert.equal(
    applies,
    1,
    "the whole point of `ui/chromeState.ts` is that there is one call site; " +
      `found ${applies}`,
  );
  assert.equal(
    [...MAIN.matchAll(/style\.display/g)].length,
    0,
    "a visibility decision came back into main.ts",
  );
  /*
   * The two control bars and their ~100 lines of CSS are gone from index.html,
   * and these were the last references to elements that no longer exist.
   *
   * Their ids are assembled rather than written out, and that is not a flourish:
   * the `ui` workstream's own gate greps the whole of `index.html` and `src/` for
   * those two ids and requires zero lines back, so a test that spelled either of
   * them would be the one thing keeping that grep red forever.
   */
  const bars = ["drive", "walk"].map((prefix) => `${prefix}-controls`);
  for (const corpse of [...bars, "renderLegend", "addGodmodeShortcut"]) {
    assert.ok(!MAIN.includes(corpse), `main.ts still references ${corpse}`);
  }
});

test("the city scene is handed the same environment rig the office is", () => {
  const scene = readFileSync(path.join(ROOT, "src/engine/scene.ts"), "utf8");
  const office = readFileSync(path.join(ROOT, "src/interiors/officeScene.ts"), "utf8");
  /*
   * The call now carries a fourth argument, and this assertion got *stronger*
   * rather than looser when it did.
   *
   * `apply` used to be unconditional everywhere it appeared, which is what let a
   * board being built off-stage dispose the visible board's probe and repoint it
   * — the rig holds one per kind and rebuilds on a key miss, and a board built
   * ahead of the camera is observed at its own centre, so its key differs by
   * construction. So the thing worth asserting is not merely that the city hands
   * its lighting to the rig; it is that every clock-driven apply is *guarded* by
   * whether anyone is looking. A future edit that drops the guard to "simplify"
   * fails here.
   */
  assert.ok(
    scene.includes('options.environment?.apply(scene, state, "city", { offstage: !presented })'),
    "the city's clock-driven apply lost its off-stage guard",
  );
  assert.ok(
    !/options\.environment\?\.apply\(scene, state, "city"\)/.test(scene),
    "an unguarded clock-driven apply is back in scene.ts",
  );
  assert.ok(scene.includes("options.environment?.release(scene)"));
  assert.ok(office.includes('options.environment?.apply(scene, state, "office")'));
  assert.ok(office.includes("options.environment?.release(scene)"));
  assert.equal(
    [...MAIN.matchAll(/createEnvironmentRig\(/g)].length,
    1,
    "one rig for the page, beside the one renderer — a rig per scene leaks a PMREM " +
      "chain and a render target on every city switch",
  );
});

test("a camera step never reaches the light, and so never reaches the environment", () => {
  /*
   * The seam this asserts, and why it is worth a source test.
   *
   * `Scene.setLighting` ends in `options.environment?.apply(scene, state, "city")`,
   * and that rig rebuilds its PMREM cubemap whenever the rig's *colours* move.
   * The camera's orbit `change` handler fires a few dozen times in one drag. So
   * long as those two are joined, every feature that makes a colour depend on
   * where the camera is standing is a frame-rate regression waiting to be
   * written, and the tempting fix — coarsening `environmentKey` — hides the
   * instance and leaves the mechanism.
   *
   * They are separate now: the camera moves two fog distances through
   * `setAerialFog`, which cannot carry a colour because `AerialFog` has no
   * colour in it. This test fails if somebody merges them back.
   */
  const scene = readFileSync(path.join(ROOT, "src/engine/scene.ts"), "utf8");

  const from = MAIN.indexOf("function applyCameraFog");
  assert.ok(from > 0, "the camera's fog path has been renamed or deleted");
  const body = MAIN.slice(from, MAIN.indexOf("\n}", from));
  assert.ok(
    !/setLighting\(/.test(body),
    "the camera path is applying a whole rig again. `setLighting` fans out across " +
      "six layers and the PMREM environment; a camera step must move fog distances " +
      "and nothing else.",
  );
  assert.ok(
    /city\.setAerialFog\(\s*atmosphere\.aerial\(/.test(body),
    "the camera path must ask `Atmosphere` for the fog — CONTRACT §4 keeps it the " +
      "sole light owner, so the app may not work a distance out for itself",
  );
  assert.ok(
    /onCameraFrame:\s*\(\)\s*=>\s*refreshAerialFog\(\)/.test(MAIN),
    "aerial fog must be on the city's tick; OrbitControls `change` is not fired by " +
      "drive, crow or aircraft follow-cameras, so a descent under those used to " +
      "step the haze at 1 Hz",
  );
  assert.ok(
    /function refreshAerialFog/.test(MAIN) &&
      /aerialFogMoved\(altitude, lastAerialFogAltitude\)/.test(MAIN),
    "the tick path must keep the 2% altitude throttle; without it a chapter flight " +
      "recomputes sixty times a second for a number that has not moved",
  );
  const sceneTick = scene.indexOf("roadTraffic?.tick(dt);");
  assert.ok(sceneTick > 0, "the drive follow camera has been renamed or deleted");
  const afterFollow = scene.slice(sceneTick, sceneTick + 800);
  assert.ok(
    /options\.onCameraFrame\?\.\(\)/.test(afterFollow),
    "onCameraFrame must run after the follow cameras write the pose, not before; " +
      "a callback above roadTraffic.tick would still see last frame's drive camera",
  );

  const setter = scene.indexOf("setAerialFog: (");
  assert.ok(setter > 0, "SceneHandle.setAerialFog has been renamed or deleted");
  const setterBody = scene.slice(setter, scene.indexOf("\n    },", setter));
  for (const forbidden of ["environment", "applyLighting", "setLighting"]) {
    assert.ok(
      !setterBody.includes(forbidden),
      `setAerialFog reached \`${forbidden}\`. It may touch the three things that draw ` +
        "fog and nothing else — the whole value of the split is that a camera step " +
        "cannot re-enter the lighting path.",
    );
  }
});

test("the aircraft pick reaches the card, and the card reaches an anonymous visitor", () => {
  const scene = readFileSync(path.join(ROOT, "src/engine/scene.ts"), "utf8");
  assert.ok(
    scene.includes("hit.object.userData.aircraftId"),
    "the city's picking must resolve an aeroplane, not only a marker",
  );
  assert.ok(
    /onAircraftPick\?\.\(/.test(scene),
    "the resolved aeroplane must reach the caller",
  );
  assert.ok(
    MAIN.includes("onAircraftPick: (a) => showAircraftDetail(a)"),
    "main.ts must wire the pick to the detail card",
  );
  // The function body, from its own `function` line to the next brace at column
  // zero. Precise enough to be worth asserting: a tier check anywhere inside it
  // is the defect, and a tier check anywhere else is somebody else's business.
  const from = MAIN.indexOf("function showAircraftDetail");
  assert.ok(from > 0, "showAircraftDetail has been renamed");
  const body = MAIN.slice(from, MAIN.indexOf("\n}", from));
  assert.ok(
    !/\baccess\./.test(body),
    "owner decision 2: the flight card is not gated on an account. An ADS-B " +
      "position is broadcast in clear to anybody with a receiver, so there is " +
      "nothing here an account could grant.",
  );
});

test("a handheld visitor reaches the texture quality that was written for them", () => {
  assert.ok(
    /deviceProfile\(\)\.handheld \? "medium" : "high"/.test(MAIN),
    "materials.ts implements low/medium/high and main.ts hardcoded `high`, so the " +
      "documented mobile escape hatch had never once been reachable",
  );
  assert.ok(
    MAIN.includes("quality: officeMaterialQuality()"),
    "the registry must be built at the quality the device profile chose",
  );
});
