// Panopticon archive — you stand at the centre, possessions ring you.
// Drag to look around · scroll to move forward · click item to inspect.

function ConcreteRoom({ items, go }) {
  const mountRef      = React.useRef(null);
  const callbackRef   = React.useRef(null);
  const navRef        = React.useRef(null); // { goTo: fn, N: number, currentIdx: fn }
  const [selectedItem, setSelectedItem] = React.useState(null);
  const [currentIdx,   setCurrentIdx]   = React.useState(0);
  callbackRef.current = setSelectedItem;

  React.useEffect(() => {
    const container = mountRef.current;
    if (!container || !window.THREE) return;
    const THREE = window.THREE;

    const H = 640;
    const W = container.offsetWidth || window.innerWidth || 1200;

    // ── Renderer ──────────────────────────────────────────────────────────
    const renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(W, H);
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
    renderer.shadowMap.enabled = true;
    renderer.shadowMap.type = THREE.PCFSoftShadowMap;
    container.appendChild(renderer.domElement);

    // ── Scene ─────────────────────────────────────────────────────────────
    const scene = new THREE.Scene();
    scene.background = new THREE.Color(0x1a1a18);
    scene.fog = new THREE.Fog(0x1a1a18, 14, 28);

    // ── Camera — first-person, centre of the panopticon ──────────────────
    const camera = new THREE.PerspectiveCamera(65, W / H, 0.05, 60);
    camera.position.set(0, 0.6, 0);

    // Euler for look-around (yaw + pitch)
    let yaw   = 0;
    let pitch = 0;
    let depth = 0; // forward/back offset along look direction

    const syncCamera = () => {
      camera.position.set(
        Math.sin(yaw) * depth * -0.1,
        0.6,
        Math.cos(yaw) * depth * -0.1
      );
      camera.rotation.order = 'YXZ';
      camera.rotation.y = yaw;
      camera.rotation.x = pitch;
    };
    syncCamera();

    // ── Material helper ───────────────────────────────────────────────────
    const lmat = hex => new THREE.MeshLambertMaterial({ color: new THREE.Color(hex) });

    // ── Floor ─────────────────────────────────────────────────────────────
    const floor = new THREE.Mesh(
      new THREE.CylinderGeometry(22, 22, 0.1, 64),
      lmat('#161614')
    );
    floor.position.y = -1.6;
    floor.receiveShadow = true;
    scene.add(floor);

    // ── Ceiling ───────────────────────────────────────────────────────────
    const ceiling = new THREE.Mesh(
      new THREE.CylinderGeometry(22, 22, 0.1, 64),
      lmat('#1c1c1a')
    );
    ceiling.position.y = 4.5;
    scene.add(ceiling);

    // ── Outer circular wall ───────────────────────────────────────────────
    // CylinderGeometry, inside surface — flip normals
    const wallGeom = new THREE.CylinderGeometry(10, 10, 7, 64, 1, true);
    // Flip normals so inside face is visible
    wallGeom.attributes.normal.array.forEach((v, i) => {
      wallGeom.attributes.normal.array[i] = -v;
    });
    wallGeom.index.array.forEach((v, i, arr) => {
      if (i % 3 === 1) { const tmp = arr[i]; arr[i] = arr[i+1]; arr[i+1] = tmp; }
    });
    const wall = new THREE.Mesh(wallGeom, lmat('#2e2e2c'));
    wall.position.y = 1.7;
    wall.receiveShadow = true;
    scene.add(wall);

    // Inner column (the tower you stand beside)
    const tower = new THREE.Mesh(
      new THREE.CylinderGeometry(0.35, 0.35, 7, 16),
      lmat('#222220')
    );
    tower.position.y = 1.7;
    tower.castShadow = true;
    scene.add(tower);

    // ── Niches + items ────────────────────────────────────────────────────
    const N      = Math.min(items.length, 24);
    const RADIUS = 8;
    const itemMeshes = [];

    for (let i = 0; i < N; i++) {
      const item  = items[i];
      const angle = (i / N) * Math.PI * 2;
      const wx    = Math.sin(angle) * RADIUS;
      const wz    = Math.cos(angle) * RADIUS;
      // Face inward (toward centre)
      const faceAngle = angle + Math.PI;

      // Niche back panel
      const nicheBack = new THREE.Mesh(
        new THREE.BoxGeometry(1.6, 2.2, 0.08),
        lmat('#262624')
      );
      nicheBack.position.set(wx * 0.94, 0.5, wz * 0.94);
      nicheBack.rotation.y = faceAngle;
      nicheBack.receiveShadow = true;
      scene.add(nicheBack);

      // Niche floor ledge
      const ledge = new THREE.Mesh(
        new THREE.BoxGeometry(1.6, 0.04, 0.35),
        lmat('#2a2a28')
      );
      ledge.position.set(wx * 0.90, -0.58, wz * 0.90);
      ledge.rotation.y = faceAngle;
      ledge.receiveShadow = true;
      scene.add(ledge);

      // Niche border lines (thin strips)
      [[1.6, 0.03, 0.04, 0, 1.12, 0], [1.6, 0.03, 0.04, 0, -1.12, 0]].forEach(([bw, bh, bd, ox, oy, oz]) => {
        const border = new THREE.Mesh(
          new THREE.BoxGeometry(bw, bh, bd),
          lmat('#3a3a38')
        );
        const dir = new THREE.Vector3(Math.sin(angle), 0, Math.cos(angle));
        border.position.set(wx * 0.93 + dir.x * ox, 0.5 + oy, wz * 0.93 + dir.z * ox);
        border.rotation.y = faceAngle;
        scene.add(border);
      });

      // Item geometry
      const hash  = [...item.id].reduce((a, c) => (a * 31 + c.charCodeAt(0)) | 0, 0);
      const s1    = 0.14 + ((hash & 0xff) / 255) * 0.38;
      const s2    = 0.10 + (((hash >> 8) & 0xff) / 255) * 0.22;

      let geom;
      if      (item.glyph === 'circle')   geom = new THREE.SphereGeometry(s2 * 0.7 + 0.13, 20, 20);
      else if (item.glyph === 'triangle') geom = new THREE.ConeGeometry(s2 * 0.55 + 0.1, s1 * 0.8 + 0.28, 4);
      else                                geom = new THREE.BoxGeometry(s2 + 0.18, s1 * 0.85 + 0.22, s2 * 0.4 + 0.1);

      const mat  = new THREE.MeshLambertMaterial({ color: new THREE.Color(item.swatch[0]) });
      const mesh = new THREE.Mesh(geom, mat);

      // Place item in front of niche back, on ledge
      const insetDist = RADIUS * 0.85;
      mesh.position.set(
        Math.sin(angle) * insetDist,
        -0.3,
        Math.cos(angle) * insetDist
      );
      mesh.castShadow = true;
      mesh.userData = { item, origColor: item.swatch[0] };
      itemMeshes.push(mesh);
      scene.add(mesh);

      // Niche number tag
      // (HTML overlay would be cleaner but we use a thin concrete slab as a placeholder)
    }

    // ── Slit light — vertical crack of light every 90° on wall ───────────
    const slitMat = new THREE.MeshBasicMaterial({ color: 0xbfecff, transparent: true, opacity: 0.5 });
    [0, Math.PI / 2, Math.PI, Math.PI * 1.5].forEach(a => {
      const s = new THREE.Mesh(new THREE.PlaneGeometry(0.06, 5), slitMat);
      s.position.set(Math.sin(a) * 9.85, 1.7, Math.cos(a) * 9.85);
      s.rotation.y = a + Math.PI;
      scene.add(s);
    });

    // ── Lighting ──────────────────────────────────────────────────────────
    // Overhead from the tower top
    const top = new THREE.PointLight(0xc8d8f0, 2.5, 18);
    top.position.set(0, 4.5, 0);
    top.castShadow = true;
    scene.add(top);

    // Warm bounce from floor
    const bounce = new THREE.PointLight(0x8090b0, 0.6, 12);
    bounce.position.set(0, -1.2, 0);
    scene.add(bounce);

    // Ambient
    scene.add(new THREE.AmbientLight(0x6878a0, 0.5));

    // ── Controls — first-person look ──────────────────────────────────────
    let isDragging = false, hasMoved = false;
    let lastX = 0, lastY = 0;

    const onDown = (e) => {
      isDragging = true; hasMoved = false;
      lastX = e.clientX; lastY = e.clientY;
      renderer.domElement.style.cursor = 'grabbing';
    };
    const onMove = (e) => {
      if (!isDragging) return;
      const dx = e.clientX - lastX;
      const dy = e.clientY - lastY;
      if (Math.abs(dx) > 2 || Math.abs(dy) > 2) hasMoved = true;
      yaw       -= dx * 0.005;
      targetYaw  = yaw; // keep in sync so lerp doesn't fight drag
      pitch      = Math.max(-0.4, Math.min(0.4, pitch - dy * 0.003));
      lastX = e.clientX; lastY = e.clientY;
      syncCamera();
    };
    const onUp = () => {
      isDragging = false;
      renderer.domElement.style.cursor = 'grab';
    };
    const onWheel = (e) => {
      depth = Math.max(-6, Math.min(6, depth + e.deltaY * 0.01));
      syncCamera();
      e.preventDefault();
    };

    renderer.domElement.addEventListener('mousedown', onDown);
    window.addEventListener('mousemove', onMove);
    window.addEventListener('mouseup', onUp);
    renderer.domElement.addEventListener('wheel', onWheel, { passive: false });
    renderer.domElement.style.cursor = 'grab';

    // ── Raycaster ─────────────────────────────────────────────────────────
    const raycaster = new THREE.Raycaster();
    const mouse = new THREE.Vector2();
    let hoveredMesh = null;

    const getNDC = (e) => {
      const r = renderer.domElement.getBoundingClientRect();
      mouse.x =  ((e.clientX - r.left) / r.width)  * 2 - 1;
      mouse.y = -((e.clientY - r.top)  / r.height)  * 2 + 1;
    };

    const onHover = (e) => {
      if (isDragging) return;
      getNDC(e);
      raycaster.setFromCamera(mouse, camera);
      const hits = raycaster.intersectObjects(itemMeshes);
      if (hits.length) {
        const hit = hits[0].object;
        if (hoveredMesh !== hit) {
          if (hoveredMesh) hoveredMesh.material.color.set(hoveredMesh.userData.origColor);
          hoveredMesh = hit;
          hoveredMesh.material.color.set(0x6699cc);
          renderer.domElement.style.cursor = 'pointer';
        }
      } else {
        if (hoveredMesh) { hoveredMesh.material.color.set(hoveredMesh.userData.origColor); hoveredMesh = null; }
        if (!isDragging) renderer.domElement.style.cursor = 'grab';
      }
    };

    const onClick = (e) => {
      if (hasMoved) return;
      getNDC(e);
      raycaster.setFromCamera(mouse, camera);
      const hits = raycaster.intersectObjects(itemMeshes);
      if (hits.length) callbackRef.current(hits[0].object.userData.item);
    };

    renderer.domElement.addEventListener('mousemove', onHover);
    renderer.domElement.addEventListener('click', onClick);

    // ── Navigation: snap to item by index ────────────────────────────────
    let targetYaw = yaw;
    let currentIdxLocal = 0;

    const goToItem = (idx) => {
      currentIdxLocal = ((idx % N) + N) % N;
      // Item i faces inward at angle + π, so camera yaw to look at it = angle
      const angle = (currentIdxLocal / N) * Math.PI * 2;
      // Normalize targetYaw so we always rotate the short way
      let delta = angle - (targetYaw % (Math.PI * 2));
      if (delta >  Math.PI) delta -= Math.PI * 2;
      if (delta < -Math.PI) delta += Math.PI * 2;
      targetYaw = targetYaw + delta;
      setCurrentIdx(currentIdxLocal);
    };

    navRef.current = {
      prev: () => goToItem(currentIdxLocal - 1),
      next: () => goToItem(currentIdxLocal + 1),
      N,
    };

    // ── Render loop with smooth yaw lerp ──────────────────────────────────
    let animId;
    const animate = () => {
      animId = requestAnimationFrame(animate);
      // Smooth lerp toward targetYaw
      const diff = targetYaw - yaw;
      if (Math.abs(diff) > 0.0005) {
        yaw += diff * 0.1;
        syncCamera();
      }
      renderer.render(scene, camera);
    };
    animate();

    // ── Resize ────────────────────────────────────────────────────────────
    const onResize = () => {
      const W2 = container.offsetWidth || 1200;
      camera.aspect = W2 / H;
      camera.updateProjectionMatrix();
      renderer.setSize(W2, H);
    };
    window.addEventListener('resize', onResize);

    return () => {
      cancelAnimationFrame(animId);
      renderer.domElement.removeEventListener('mousedown', onDown);
      window.removeEventListener('mousemove', onMove);
      window.removeEventListener('mouseup', onUp);
      renderer.domElement.removeEventListener('wheel', onWheel);
      renderer.domElement.removeEventListener('mousemove', onHover);
      renderer.domElement.removeEventListener('click', onClick);
      window.removeEventListener('resize', onResize);
      renderer.dispose();
      if (container.contains(renderer.domElement)) container.removeChild(renderer.domElement);
    };
  }, []);

  return (
    <div style={{ position: 'relative', userSelect: 'none' }}>
      <div ref={mountRef} style={{ width: '100%', height: 640, display: 'block', background: '#1a1a18' }} />

      <div className="mono" style={{
        position: 'absolute', top: 20, left: 24, fontSize: 10,
        letterSpacing: '0.28em', textTransform: 'uppercase',
        color: 'rgba(191,236,255,0.45)', pointerEvents: 'none',
      }}>drag · scroll · click</div>

      {/* Left arrow */}
      <button onClick={() => navRef.current && navRef.current.prev()} style={{
        position: 'absolute', left: 0, top: '50%', transform: 'translateY(-50%)',
        width: 56, height: 80,
        background: 'rgba(14,14,12,0.55)', border: 'none',
        color: '#6699CC', fontSize: 22, cursor: 'pointer',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        transition: 'background 0.15s',
      }}
        onMouseEnter={e => e.currentTarget.style.background = 'rgba(102,153,204,0.18)'}
        onMouseLeave={e => e.currentTarget.style.background = 'rgba(14,14,12,0.55)'}
      >←</button>

      {/* Right arrow */}
      <button onClick={() => navRef.current && navRef.current.next()} style={{
        position: 'absolute', right: selectedItem ? 300 : 0, top: '50%', transform: 'translateY(-50%)',
        width: 56, height: 80,
        background: 'rgba(14,14,12,0.55)', border: 'none',
        color: '#6699CC', fontSize: 22, cursor: 'pointer',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        transition: 'background 0.15s, right 0.25s',
      }}
        onMouseEnter={e => e.currentTarget.style.background = 'rgba(102,153,204,0.18)'}
        onMouseLeave={e => e.currentTarget.style.background = 'rgba(14,14,12,0.55)'}
      >→</button>

      {/* Item counter */}
      <div className="mono" style={{
        position: 'absolute', bottom: 90, left: '50%', transform: 'translateX(-50%)',
        fontSize: 10, letterSpacing: '0.28em', textTransform: 'uppercase',
        color: 'rgba(191,236,255,0.5)', pointerEvents: 'none',
      }}>
        {String(currentIdx + 1).padStart(2, '0')} / {navRef.current ? String(navRef.current.N).padStart(2, '0') : '--'}
      </div>

      {/* Info panel */}
      {selectedItem && (
        <div style={{
          position: 'absolute', top: 0, right: 0, width: 300, height: 640,
          background: 'rgba(14,14,12,0.97)',
          borderLeft: '1px solid rgba(102,153,204,0.22)',
          padding: '28px 24px', boxSizing: 'border-box',
          overflowY: 'auto', display: 'flex', flexDirection: 'column',
        }}>
          <button onClick={() => setSelectedItem(null)} style={{
            position: 'absolute', top: 14, right: 16,
            background: 'none', border: 'none', color: '#8C8C8C',
            fontSize: 22, cursor: 'pointer', lineHeight: 1,
          }}>×</button>

          <div className="mono" style={{ fontSize: 9, color: '#6699CC', letterSpacing: '0.32em', textTransform: 'uppercase', marginBottom: 14 }}>
            {selectedItem.category} · {selectedItem.year}
          </div>
          <div className="display" style={{ fontSize: 20, color: '#EFEFED', lineHeight: 1.1, marginBottom: 20 }}>
            {selectedItem.name}
          </div>

          {[['Material', selectedItem.material], ['Location', selectedItem.location], ['To', selectedItem.beneficiary], ['Delivery', selectedItem.delivery]].map(([l, v]) => (
            <div key={l} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', padding: '9px 0', borderBottom: '1px dotted rgba(255,255,255,0.07)', gap: 12 }}>
              <span className="mono" style={{ fontSize: 9, color: '#8C8C8C', letterSpacing: '0.22em', textTransform: 'uppercase', flexShrink: 0 }}>{l}</span>
              <span style={{ fontSize: 11, color: '#EFEFED', textAlign: 'right', lineHeight: 1.4 }}>{v}</span>
            </div>
          ))}

          <div style={{ marginTop: 20, padding: '14px 16px', borderLeft: '2px solid #6699CC', background: 'rgba(102,153,204,0.06)' }}>
            <div className="mono" style={{ fontSize: 9, color: '#6699CC', letterSpacing: '0.28em', textTransform: 'uppercase', marginBottom: 10 }}>Last will</div>
            <div className="serif" style={{ fontSize: 13, color: '#D8D8D6', lineHeight: 1.75, fontStyle: 'italic', fontWeight: 300 }}>
              "{selectedItem.will}"
            </div>
          </div>

          <button onClick={() => go('item', selectedItem)} style={{
            marginTop: 24, width: '100%', padding: '13px',
            background: 'transparent', color: '#6699CC',
            border: '1px solid rgba(102,153,204,0.35)',
            fontFamily: 'JetBrains Mono, monospace', fontSize: 10,
            letterSpacing: '0.22em', textTransform: 'uppercase', cursor: 'pointer',
          }}>open full bequest →</button>
        </div>
      )}

      <div style={{
        position: 'absolute', bottom: 0, left: 0, right: 0, height: 80,
        background: 'linear-gradient(to top, var(--paper-2) 0%, transparent 100%)',
        pointerEvents: 'none',
      }} />
    </div>
  );
}

Object.assign(window, { ConcreteRoom });
