/**
 * The reconciliation, and the one property that protects the live site: with
 * the flag off, nothing anywhere is different.
 *
 * "Byte-identical with the flag off" is asserted here rather than only in a
 * photograph because a photograph proves it for one frame of one board on one
 * afternoon, and this proves it for the object the engine reads. `reconciledCity`
 * returning its argument **by identity** is the strongest form of that claim
 * available: not a copy that happens to carry the same numbers, the same object.
 */

import assert from "node:assert/strict";
import { afterEach, describe, it } from "node:test";
import CALIFORNIA from "../cities/california.ts";
import {
  COAST_RAMP_CELLS,
  RELIEF_IN_FRAME,
  blendedPeakMetres,
  describe as describeReconciliation,
  reconciledCity,
  reconciledCoastFalloff,
  reconciledExaggeration,
  reconciledPalette,
  reconciledPaletteMix,
  setReconcile,
} from "../cities/reconcile.ts";
import SF from "../cities/sf.ts";
import SOCAL from "../cities/socal.ts";
import { DEFAULT_PALETTE } from "../engine/terrain.ts";
import { World } from "../engine/world.ts";

const PACKS = [CALIFORNIA, SF, SOCAL];

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

describe("reconcile — the flag off", () => {
  it("hands every pack back by identity", () => {
    setReconcile(false);
    for (const pack of PACKS) assert.equal(reconciledCity(pack), pack);
  });

  it("leaves the World's derived scalars exactly where they were", () => {
    setReconcile(false);
    for (const pack of PACKS) {
      const world = new World(pack);
      assert.equal(world.city, pack);
      assert.equal(world.lngScale, pack.latScale * Math.cos((pack.center.lat * Math.PI) / 180));
      assert.equal(world.lngSquash, world.lngScale / pack.latScale);
      assert.equal(world.metresPerUnit, 111_320 / pack.latScale);
      assert.equal(world.city.verticalExaggeration, pack.verticalExaggeration);
      assert.equal(world.city.coastFalloff, pack.coastFalloff);
      assert.deepEqual(
        world.city.roads.map((road) => road.width),
        pack.roads.map((road) => road.width),
      );
    }
  });

  /**
   * `lngSquash` stopped being `cos(centre.lat)` and became `lngScale/latScale`,
   * which is the same number for every authored pack and the right one for a
   * reconciled one. This is the assertion that the "same number" half is true —
   * it is what every hill on every shipped board stands on.
   */
  it("computes the same hill squash the old expression did", () => {
    setReconcile(false);
    for (const pack of PACKS) {
      const world = new World(pack);
      const old = Math.cos((pack.center.lat * Math.PI) / 180);
      assert.ok(Math.abs(world.lngSquash - old) < 1e-15, `${pack.id}: ${world.lngSquash} vs ${old}`);
    }
  });
});

describe("reconcile — one relief rule", () => {
  /**
   * The whole claim of rule (a), as one number per board.
   *
   * Relief in the frame is peak scene units over board span — ARCHITECTURE
   * §12.1's rule, and the one it moved California from 13 to 15 to hold. Today
   * the three boards read 7.24%, 4.51% and 6.92%; the rule puts all three on
   * `RELIEF_IN_FRAME`.
   */
  it("puts all three boards on one relief-in-frame number", () => {
    setReconcile(["exaggeration", "projection"]);
    const relief = PACKS.map((pack) => {
      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,
      );
      return world.metres(blendedPeakMetres(pack)) / span;
    });
    for (const value of relief) assert.ok(Math.abs(value - RELIEF_IN_FRAME) < 1e-6, String(value));
  });

  /**
   * The board ARCHITECTURE §12.1 tuned against a photograph does not move, and
   * that is not a coincidence: `RELIEF_IN_FRAME` is California's own number,
   * rounded to three significant figures. The 0.1% here is that rounding.
   */
  it("leaves California where the photograph put it", () => {
    assert.ok(Math.abs(reconciledExaggeration(CALIFORNIA) - 15) < 0.015);
  });

  /**
   * The honest limit of rule (a), asserted so nobody assumes otherwise.
   *
   * Apparent relief at a *matched* pose — the same true-metre stand-off on both
   * boards, which is what the seam actually is — is proportional to the
   * exaggeration itself and not to relief-in-frame. The rule takes the Bay Area
   * direction from 4.17× to 2.60× and leaves the Southern California direction
   * at 4.4×, because those two boards were already on one number. Only a single
   * exaggeration everywhere would make it 1.0×, and that flattens the state to
   * 1.7% of its own frame.
   */
  it("narrows the seam step without closing it", () => {
    setReconcile(["exaggeration", "projection"]);
    const ex = (pack: typeof CALIFORNIA): number => new World(pack).city.verticalExaggeration;
    assert.ok(ex(CALIFORNIA) / ex(SF) < 4.167, "the Bay Area direction must improve");
    assert.ok(Math.abs(ex(CALIFORNIA) / ex(SF) - 2.6) < 0.1);
    assert.ok(Math.abs(ex(CALIFORNIA) / ex(SOCAL) - 4.41) < 0.1);
  });
});

describe("reconcile — one ground convention", () => {
  it("makes the coastal ramp a fixed multiple of the coarse cell", () => {
    for (const pack of PACKS) {
      const cell = pack.cellLat * (pack.coarseFactor ?? 1);
      assert.ok(Math.abs(reconciledCoastFalloff(pack) / cell - COAST_RAMP_CELLS) < 1e-12);
    }
  });

  /**
   * The rule is California's own ratio, so the state board's 0.025° comes back
   * out of it to within 0.2%. That is what makes it a rule found rather than a
   * rule invented.
   */
  it("reproduces California's authored falloff", () => {
    assert.ok(Math.abs(reconciledCoastFalloff(CALIFORNIA) / CALIFORNIA.coastFalloff - 1) < 0.002);
  });

  it("only ever widens the metro ramps, which is the direction that hides steps", () => {
    for (const pack of [SF, SOCAL]) assert.ok(reconciledCoastFalloff(pack) > pack.coastFalloff);
  });

  /** The north anchor is `DEFAULT_PALETTE`, copied. This is the copy not drifting. */
  it("keeps its copy of DEFAULT_PALETTE honest", () => {
    setReconcile(["ground"]);
    const north = reconciledPalette({ ...SF, bounds: { ...SF.bounds, minLat: 90, maxLng: -122 } });
    for (const [key, value] of Object.entries(DEFAULT_PALETTE)) {
      assert.equal(north[key as keyof typeof north], value, key);
    }
  });

  it("gives California its own palette back, exactly", () => {
    const mixed = reconciledPalette(CALIFORNIA);
    assert.equal(reconciledPaletteMix(CALIFORNIA), 1);
    for (const [key, value] of Object.entries(CALIFORNIA.palette ?? {})) {
      assert.equal(mixed[key as keyof typeof mixed], value, key);
    }
  });

  /**
   * `alpine` is a fact about the board, not a taste: it is the colour of ground
   * above the snow line, so a board with no ground up there does not get one.
   * San Francisco's tallest is 1,186 m against a 1,900 m threshold.
   */
  it("hands out the alpine stop only to a board that reaches the snow line", () => {
    assert.equal(reconciledPalette(SF).alpine, undefined);
    assert.ok(reconciledPalette(SOCAL).alpine !== undefined);
    assert.ok(reconciledPalette(CALIFORNIA).alpine !== undefined);
  });
});

describe("reconcile — the mechanics", () => {
  it("is idempotent, which is what the terrain worker's second pass depends on", () => {
    setReconcile(true);
    for (const pack of PACKS) {
      const once = reconciledCity(pack);
      const clone = JSON.parse(JSON.stringify(once)) as typeof once;
      assert.equal(reconciledCity(once), once);
      assert.equal(reconciledCity(clone), clone);
      assert.equal(clone.reconciled, true);
    }
  });

  it("returns one object per pack and rule set, so a rebuild agrees with itself", () => {
    setReconcile(["roads"]);
    const roads = reconciledCity(SF);
    assert.equal(reconciledCity(SF), roads);
    setReconcile(["roads", "ground"]);
    assert.notEqual(reconciledCity(SF), roads);
    setReconcile(["roads"]);
    assert.equal(reconciledCity(SF), roads);
  });

  it("turns each rule on alone", () => {
    setReconcile(["roads"]);
    const roadsOnly = reconciledCity(CALIFORNIA);
    assert.equal(roadsOnly.verticalExaggeration, CALIFORNIA.verticalExaggeration);
    assert.equal(roadsOnly.coastFalloff, CALIFORNIA.coastFalloff);
    assert.equal(roadsOnly.lngScale, undefined);
    assert.notEqual(roadsOnly.roads[0]?.width, CALIFORNIA.roads[0]?.width);

    setReconcile(["exaggeration"]);
    const reliefOnly = reconciledCity(SF);
    assert.notEqual(reliefOnly.verticalExaggeration, SF.verticalExaggeration);
    assert.deepEqual(
      reliefOnly.roads.map((r) => r.width),
      SF.roads.map((r) => r.width),
    );
  });

  it("reports what it did", () => {
    const report = describeReconciliation(SOCAL);
    assert.equal(report.id, "socal");
    assert.ok(report.lngMetresPerDegree.before > report.lngMetresPerDegree.after);
    assert.ok(report.roadMetres.after[0] > 30 && report.roadMetres.after[1] < 56);
  });
});
