import {
  Engine,
  Scene,
  ArcRotateCamera,
  HemisphericLight,
  DirectionalLight,
  PointLight,
  MeshBuilder,
  StandardMaterial,
  Vector3,
  Color3,
  Color4,
} from '@babylonjs/core';

/**
 * Owns the ONE Babylon Engine + Scene + ArcRotateCamera for Viewport V2.
 * No module may create a second engine. The camera attaches only to the real
 * V2 canvas, and only after the canvas is confirmed mounted + connected + non-zero.
 */
export function createBabylonContext(canvas) {
  if (!canvas || !canvas.isConnected) return null;
  const rect = canvas.getBoundingClientRect();
  if (!rect || rect.width <= 0 || rect.height <= 0) return null;

  const engine = new Engine(canvas, true, {
    preserveDrawingBuffer: false,
    stencil: true,
    disableWebGL2Support: false,
  });

  const scene = new Scene(engine);
  scene.clearColor = new Color4(0.043, 0.051, 0.078, 1);

  const camera = new ArcRotateCamera(
    'limbforgeCamera',
    -1.15,
    1.12,
    8.5,
    new Vector3(0, 3.0, 0),
    scene
  );
  camera.minZ = 0.1;
  camera.lowerRadiusLimit = 2;
  camera.upperRadiusLimit = 30;
  camera.wheelDeltaPercentage = 0.01;
  // Attach only to the confirmed real V2 canvas.
  camera.attachControl(canvas, true);

  const hemi = new HemisphericLight('hemi', new Vector3(0, 1, 0), scene);
  hemi.intensity = 0.95;
  hemi.diffuse = Color3.FromHexString('#C8D0DC');
  hemi.groundColor = Color3.FromHexString('#2A1B4A');

  const dir = new DirectionalLight('dir', new Vector3(-0.5, -1, -0.3), scene);
  dir.intensity = 0.95;
  dir.diffuse = Color3.FromHexString('#ffffff');

  // Subtle SHiRE fill + rim lights to reveal anatomy (no shaders, no post-processing).
  const purpleFill = new PointLight('purple-fill', new Vector3(-2.6, 4.4, 2.4), scene);
  purpleFill.diffuse = Color3.FromHexString('#7E3FF2');
  purpleFill.intensity = 0.42;

  const greenRim = new PointLight('green-rim', new Vector3(2.8, 3.2, -3.0), scene);
  greenRim.diffuse = Color3.FromHexString('#22D37D');
  greenRim.intensity = 0.32;

  // Dark ground plane + ordinary line grid (no shader grid).
  const ground = MeshBuilder.CreateBox('ground', { width: 30, depth: 30, height: 0.08 }, scene);
  ground.position.y = -0.04;
  const gMat = new StandardMaterial('groundMat', scene);
  gMat.diffuseColor = Color3.FromHexString('#0b0d14');
  gMat.specularColor = new Color3(0, 0, 0);
  ground.material = gMat;
  ground.isPickable = false;

  const grid = buildGrid(scene);

  return { engine, scene, camera, hemi, dir, ground, grid, canvas };
}

function buildGrid(scene) {
  const lines = [];
  const step = 2;
  const extent = 14;
  for (let i = -extent; i <= extent; i += step) {
    lines.push([new Vector3(i, 0.012, -extent), new Vector3(i, 0.012, extent)]);
    lines.push([new Vector3(-extent, 0.012, i), new Vector3(extent, 0.012, i)]);
  }
  const grid = MeshBuilder.CreateLineSystem('grid', { lines }, scene);
  grid.color = Color3.FromHexString('#221a33');
  grid.isPickable = false;
  return grid;
}

/**
 * Cleanup order: detach controls -> dispose scene -> dispose engine.
 * Called AFTER the component stops the render loop and removes observers/listeners.
 * Idempotent; never disposes twice.
 */
export function destroyBabylonContext(ctx) {
  if (!ctx) return;
  const { engine, scene, canvas, camera } = ctx;
  try { if (camera) camera.detachControl(canvas); } catch (_) {}
  try { if (scene) scene.dispose(); } catch (_) {}
  try { if (engine) engine.dispose(); } catch (_) {}
}