import { expect } from "@esm-bundle/chai";
import { VoxelManager } from "../../src/grid/VoxelManager.js";
import { VoxelGrid } from "../../src/grid/VoxelGrid.js";
import * as THREE from "three";

describe("Phase 1: VoxelManager Rendering (WebGL)", function () {
  // Increase timeout to 60s for slow WebGL initialization in headless/software mode
  this.timeout(60000);

  let grid;
  let scene;
  let manager;
  let renderer;

  before(function () {
    // Allow extra time specifically for the first context creation
    this.timeout(30000);

    // 1. Setup a real WebGL Renderer (Headless)
    const canvas = document.createElement("canvas");

    // FORCE WebGL 2 (Required for Three.js r163+)
    const context = canvas.getContext("webgl2");

    if (!context) {
      console.warn(
        "WebGL 2 not supported in this test environment. Skipping render checks."
      );
      this.skip();
      return;
    }

    renderer = new THREE.WebGLRenderer({ canvas, context });
  });

  after(function () {
    if (renderer) {
      renderer.dispose();
      // Explicitly force context loss to free up resources for other test files
      if (renderer.forceContextLoss) renderer.forceContextLoss();
    }
  });

  beforeEach(() => {
    grid = new VoxelGrid(4, 4, 4);
    scene = new THREE.Scene();
    manager = new VoxelManager(grid, scene);
  });

  it("CoA 1: update() should create separate InstancedMeshes for different IDs", () => {
    grid.setCell(0, 0, 0, 1); // ID 1 (Stone)
    grid.setCell(1, 0, 0, 2); // ID 2 (Dirt)

    // In the new multi-material manager, update() handles initialization
    manager.update();

    // Should have 2 children in the scene (one mesh per ID) + Focus Target
    // Filter to just find meshes
    const meshes = scene.children.filter((c) => c.isInstancedMesh);
    expect(meshes.length).to.equal(2);
  });

  it("CoA 2: update() should correctly position instances", () => {
    grid.setCell(2, 2, 2, 1); // Specific position
    manager.update();

    // Find the mesh corresponding to ID 1
    const mesh = scene.children.find((c) => c.isInstancedMesh);
    const matrix = new THREE.Matrix4();

    // Since there is only 1 voxel of ID 1, it will be at instance index 0
    mesh.getMatrixAt(0, matrix);

    const position = new THREE.Vector3().setFromMatrixPosition(matrix);

    expect(position.x).to.equal(2);
    expect(position.y).to.equal(2);
    expect(position.z).to.equal(2);
  });

  it("CoA 3: render loop should not crash", () => {
    // Verify we can actually call render() without WebGL errors
    const camera = new THREE.PerspectiveCamera();
    manager.update();

    expect(() => renderer.render(scene, camera)).to.not.throw();
  });

  it("CoA 4: updateMaterials() should apply texture to material", () => {
    // Mock a generated asset
    const mockCanvas = document.createElement("canvas");
    const assets = {
      textures: {
        floor: mockCanvas,
      },
    };

    // Create a floor voxel (ID 2)
    grid.setCell(0, 0, 0, 2);

    manager.updateMaterials(assets);

    const mesh = scene.children.find((c) => c.isInstancedMesh);
    // Material could be single or array (multi-material)
    // ID 2 uses an array [side, side, top, bottom, front, back]
    const mat = Array.isArray(mesh.material) ? mesh.material[2] : mesh.material;

    expect(mat.map).to.exist;
    expect(mat.map.image).to.equal(mockCanvas);
  });

  it("CoA 5: Should create a Focus Target at the center of the grid", () => {
    // Grid is 4x4x4. Center is roughly 2, 0, 2.
    manager.update();

    expect(manager.focusTarget).to.exist;
    expect(manager.focusTarget.parent).to.equal(scene);

    expect(manager.focusTarget.position.x).to.equal(2);
    expect(manager.focusTarget.position.z).to.equal(2);
  });
});
