import assert from "node:assert/strict";
import { describe, it } from "node:test";
import * as THREE from "three";
import { createAssetContext, kit } from "../assets/kit.ts";
import { MaterialRegistry } from "../assets/materials.ts";
import "../assets/office/index.ts";

const HABITAT_IDS = [
  "tera:bed.platform",
  "tera:sofa.modular",
  "tera:kitchen.run",
  "tera:kitchen.island",
  "tera:storage.wardrobe",
  "tera:seat.stool",
  "tera:light.floor",
] as const;

function seeded(): () => number {
  let value = 0x12345678;
  return () => {
    value = (Math.imul(value, 1664525) + 1013904223) >>> 0;
    return value / 0x1_0000_0000;
  };
}

function disposeObject(object: THREE.Object3D): void {
  object.traverse((child) => {
    if (child instanceof THREE.Mesh) child.geometry.dispose();
  });
}

describe("procedural habitat assets", () => {
  it("registers finite metre-scale footprints for the complete studio kit", () => {
    for (const id of HABITAT_IDS) {
      assert.equal(kit.has(id), true, `${id} is not registered`);
      const footprint = kit.footprintOf(id);
      for (const value of [footprint.width, footprint.depth, footprint.height]) {
        assert.ok(Number.isFinite(value) && value > 0 && value < 5, `${id} has invalid footprint ${value}`);
      }
    }
  });

  it("builds deterministic, non-placeholder geometry with finite bounds", () => {
    const materials = new MaterialRegistry({ quality: "low" });
    try {
      for (const id of HABITAT_IDS) {
        const first = kit.build(id, createAssetContext({ materials, rand: seeded() }));
        const second = kit.build(id, createAssetContext({ materials, rand: seeded() }));
        assert.equal(first.userData.assetId, id);
        assert.notEqual(first.userData.missing, true);
        const firstBox = new THREE.Box3().setFromObject(first);
        const secondBox = new THREE.Box3().setFromObject(second);
        const encode = (box: THREE.Box3) => [...box.min.toArray(), ...box.max.toArray()];
        assert.deepEqual(encode(firstBox), encode(secondBox), `${id} is not deterministic`);
        assert.ok(encode(firstBox).every(Number.isFinite), `${id} has non-finite bounds`);
        assert.ok(firstBox.max.y > 0, `${id} has no visible height`);
        disposeObject(first);
        disposeObject(second);
      }
    } finally {
      materials.dispose();
    }
  });
});
