import assert from "node:assert/strict";
import { describe, it } from "node:test";
import * as THREE from "three";
import { MaterialRegistry } from "../assets/materials.ts";
import "../assets/office/index.ts";
import { createFurnishings } from "../interiors/furnish.ts";
import { Plan } from "../interiors/plan.ts";
import type { Level, Office, Room } from "../interiors/types.ts";
import { createOfficeMediaPresentation } from "../media/presentation.ts";

const ROOM: Room = {
  id: "room",
  name: "Room",
  floor: "floor" as never,
  outline: [{ x: 0, z: 0 }, { x: 8, z: 0 }, { x: 8, z: 6 }, { x: 0, z: 6 }],
};

function fixture(depth: "full" | "public" = "full") {
  const level: Level = {
    id: "ground",
    name: "Ground",
    elevation: 0,
    wallHeight: 3,
    wallThickness: 0.1,
    floorplan: { rooms: [ROOM], walls: [], props: [
      { id: "wall-display", kind: "tera:screen.wall-display", position: { x: 2, z: 2 }, rotation: 0 },
      { id: "desk-monitor", kind: "tera:screen.monitor", position: { x: 4, z: 2 }, rotation: 0, elevation: 0.73 },
      { id: "ordinary-desk", kind: "tera:desk.workstation", position: { x: 6, z: 2 }, rotation: 0 },
      {
        id: "private-display",
        kind: "tera:screen.wall-display",
        position: { x: 2, z: 4 },
        rotation: 0,
        audience: "private",
      },
    ] },
  };
  const office: Office = { id: "media-office", name: "Media Office", levels: [level], viewpoints: [] };
  const plan = new Plan(office, { depth, warn: false });
  const materials = new MaterialRegistry({ quality: "low" });
  const furnishings = createFurnishings(plan, { materials });
  const presentation = createOfficeMediaPresentation(plan, furnishings.group);
  return { plan, materials, furnishings, presentation };
}

describe("procedural office media presentation", () => {
  it("discovers only authored screen props and keeps exact prop ids and rooms", () => {
    const f = fixture();
    const listed = f.presentation.list();
    assert.deepEqual(listed.map((item) => item.screenId).sort(), ["desk-monitor", "private-display", "wall-display"]);
    assert.ok(listed.every((item) => item.officeId === "media-office" && item.roomId === "room"));
    assert.ok(!listed.some((item) => item.screenId === "ordinary-desk"));
    assert.equal(f.presentation.group.children.length, 3);
    f.presentation.dispose();
    f.furnishings.dispose();
    f.materials.dispose();
  });

  it("keeps public depth private-prop-free by discovering only the resolved plan", () => {
    const f = fixture("public");
    assert.deepEqual(f.presentation.list().map((item) => item.screenId).sort(), ["desk-monitor", "wall-display"]);
    f.presentation.dispose();
    f.furnishings.dispose();
    f.materials.dispose();
  });

  it("requires authorization plus opt-in and returns to a safe placeholder", () => {
    const f = fixture();
    let textureDisposals = 0;
    const texture = new THREE.VideoTexture({} as HTMLVideoElement);
    texture.dispose = () => { textureDisposals += 1; };
    assert.equal(f.presentation.bind("wall-display", { canView: true, optedIn: false }, texture), false);
    assert.equal(f.presentation.list().find((item) => item.screenId === "wall-display")?.bound, false);
    assert.equal(f.presentation.bind("wall-display", { canView: true, optedIn: true }, texture), true);
    const mesh = f.presentation.group.getObjectByName("media-surface:wall-display") as THREE.Mesh;
    assert.equal((mesh.material as THREE.MeshBasicMaterial).map, texture);
    assert.equal(f.presentation.clear("wall-display"), true);
    assert.equal((mesh.material as THREE.MeshBasicMaterial).map, null);
    assert.equal(textureDisposals, 0);
    f.presentation.dispose();
    assert.equal(textureDisposals, 0, "caller-owned texture was disposed");
    f.furnishings.dispose();
    f.materials.dispose();
  });

  it("returns defensive descriptors and contains unknown/disposed operations", () => {
    const f = fixture();
    const leaked = f.presentation.list()[0];
    assert.ok(leaked);
    leaked.screenId = "changed";
    assert.notEqual(f.presentation.list()[0]?.screenId, "changed");
    const texture = new THREE.VideoTexture({} as HTMLVideoElement);
    assert.equal(f.presentation.bind("missing", { canView: true, optedIn: true }, texture), false);
    f.presentation.dispose();
    f.presentation.dispose();
    assert.equal(f.presentation.bind("wall-display", { canView: true, optedIn: true }, texture), false);
    assert.equal(f.presentation.clear("wall-display"), false);
    f.furnishings.dispose();
    f.materials.dispose();
    texture.dispose();
  });
});
