import { useEffect, useRef } from 'react'
import * as THREE from 'three'

// A self-contained three.js stage that slowly turns a single model and lets the viewer
// swipe to spin it faster, the momentum easing back to the idle rate — the same feel as
// the inventory rack, reused for one-off showpieces like a merit award.
export function SpinningModel({ build, className, cameraPos = [0.6, 0.4, 5], baseSpin = 0.008 }: {
  build: () => THREE.Group
  className?: string
  cameraPos?: [number, number, number]
  baseSpin?: number
}) {
  const stageRef = useRef<HTMLDivElement>(null)
  const [camX, camY, camZ] = cameraPos

  useEffect(() => {
    const stage = stageRef.current
    if (!stage) return
    const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
    stage.appendChild(renderer.domElement)
    renderer.domElement.style.cssText = 'position:absolute;inset:0;width:100%;height:100%'

    const scene = new THREE.Scene()
    const camera = new THREE.PerspectiveCamera(34, 1, 0.1, 100)
    camera.position.set(camX, camY, camZ)
    camera.lookAt(0, 0, 0)
    scene.add(new THREE.AmbientLight(0x40483a, 0.9))
    const key = new THREE.DirectionalLight(0xfff4e0, 1.1); key.position.set(2, 3, 3); scene.add(key)
    const rim = new THREE.DirectionalLight(0x9fd020, 0.4); rim.position.set(-2, 1, -2); scene.add(rim)
    const holder = new THREE.Group(); holder.add(build()); scene.add(holder)

    const resize = () => { const w = stage.clientWidth, h = stage.clientHeight; if (w && h) { camera.aspect = w / h; camera.updateProjectionMatrix(); renderer.setSize(w, h, false) } }
    resize(); const ro = new ResizeObserver(resize); ro.observe(stage)

    // Drag to spin (see inventory rack): a swipe rotates 1:1 and hands its speed to `spin`.
    let spin = baseSpin
    let dragging = false
    let lastX = 0
    const dom = renderer.domElement
    dom.style.touchAction = 'none'
    dom.style.cursor = 'grab'
    const onDown = (event: PointerEvent) => { dragging = true; lastX = event.clientX; dom.style.cursor = 'grabbing'; dom.setPointerCapture(event.pointerId) }
    const onMove = (event: PointerEvent) => {
      if (!dragging) return
      const delta = (event.clientX - lastX) * 0.01
      lastX = event.clientX
      holder.rotation.y += delta
      spin = THREE.MathUtils.clamp(delta, -0.35, 0.35)
    }
    const onUp = (event: PointerEvent) => { dragging = false; dom.style.cursor = 'grab'; try { dom.releasePointerCapture(event.pointerId) } catch { /* already released */ } }
    dom.addEventListener('pointerdown', onDown)
    dom.addEventListener('pointermove', onMove)
    dom.addEventListener('pointerup', onUp)
    dom.addEventListener('pointercancel', onUp)

    let raf = 0
    const tick = () => {
      if (!dragging) { holder.rotation.y += spin; spin += (baseSpin - spin) * 0.04 }
      renderer.render(scene, camera); raf = requestAnimationFrame(tick)
    }
    tick()
    return () => {
      cancelAnimationFrame(raf); ro.disconnect()
      dom.removeEventListener('pointerdown', onDown); dom.removeEventListener('pointermove', onMove)
      dom.removeEventListener('pointerup', onUp); dom.removeEventListener('pointercancel', onUp)
      renderer.dispose(); dom.remove()
      scene.traverse(o => { if (o instanceof THREE.Mesh) { o.geometry.dispose(); (o.material as THREE.Material).dispose() } })
    }
  }, [build, camX, camY, camZ, baseSpin])

  return <div ref={stageRef} className={className || 'model3d-stage'} />
}
