import { useEffect, useRef, useState } from 'react'
import * as THREE from 'three'
import { buildPhone, CLOSED_ANGLE, PhonePreview, type PhoneSession } from './phone'

// A reusable "tools" inventory: a three.js rack you cycle through, one 3D tool at a
// time, then USE to open it. Tool-driven (see TOOLS below), so it isn't tied to any
// level type — new tools (map, visual novel, …) just register a model + a component.
// Rendered as a dev overlay at /?inventory=1 for now; drop <Inventory/> into the
// real navbar later.

function buildPhoneModel() {
  const { group, hinge } = buildPhone()
  hinge.rotation.x = CLOSED_ANGLE // sit closed on the rack
  group.scale.setScalar(1.15)
  group.position.y = 0.5 // the phone's mass hangs below its pivot — lift it to centre in frame
  return group
}

function buildNotebook() {
  const g = new THREE.Group()
  const coverMat = new THREE.MeshStandardMaterial({ color: 0x7c3a2b, roughness: 0.85, metalness: 0.05, flatShading: true })
  const pageMat = new THREE.MeshStandardMaterial({ color: 0xe7dcbf, roughness: 0.95, flatShading: true })
  const wireMat = new THREE.MeshStandardMaterial({ color: 0xcaa74a, roughness: 0.5, metalness: 0.5, flatShading: true })
  const back = new THREE.Mesh(new THREE.BoxGeometry(0.86, 1.16, 0.05), coverMat); back.position.z = -0.08; g.add(back)
  const pages = new THREE.Mesh(new THREE.BoxGeometry(0.8, 1.08, 0.12), pageMat); g.add(pages)
  const front = new THREE.Mesh(new THREE.BoxGeometry(0.86, 1.16, 0.05), coverMat); front.position.z = 0.09; g.add(front)
  const strap = new THREE.Mesh(new THREE.BoxGeometry(0.06, 1.2, 0.02), new THREE.MeshStandardMaterial({ color: 0x2a2320, roughness: 0.8, flatShading: true }))
  strap.position.set(0.3, 0, 0.12); g.add(strap)
  for (let i = 0; i < 8; i++) { // spiral binding down the spine
    const ring = new THREE.Mesh(new THREE.TorusGeometry(0.035, 0.012, 6, 10), wireMat)
    ring.position.set(-0.43, 0.49 - i * 0.14, 0); ring.rotation.y = Math.PI / 2; g.add(ring)
  }
  g.traverse(obj => { if (obj instanceof THREE.Mesh) obj.castShadow = true })
  return g
}

const TOOLS: { key: string; name: string; build: () => THREE.Group }[] = [
  { key: 'notebook', name: 'Notebook', build: buildNotebook },
  { key: 'phone', name: 'Phone', build: buildPhoneModel },
]

function ToolRack({ index }: { index: number }) {
  const stageRef = useRef<HTMLDivElement>(null)
  const holderRef = useRef<THREE.Group | null>(null)

  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)
    // Pulled back along the same view direction (was z 3.3) so the tools sit smaller in
    // frame — full-screen models were hard to read as objects.
    camera.position.set(0.63, 0.57, 8.15)
    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(); scene.add(holder); holderRef.current = 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: a swipe rotates the holder 1:1 with the pointer and hands its speed
    // to `spin`, which the idle loop keeps applying and eases back to the resting rate —
    // so interfering with a flick sends it spinning faster, then it settles.
    const BASE_SPIN = 0.008
    let spin = BASE_SPIN
    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) // carry the swipe's speed as momentum
    }
    const onUp = (event: PointerEvent) => { dragging = false; dom.style.cursor = 'grab'; try { dom.releasePointerCapture(event.pointerId) } catch { /* pointer 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 += (BASE_SPIN - spin) * 0.04 } // momentum eases back to idle
      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(); renderer.domElement.remove()
      scene.traverse(o => { if (o instanceof THREE.Mesh) { o.geometry.dispose(); (o.material as THREE.Material).dispose() } })
      holderRef.current = null
    }
  }, [])

  // Swap the model when the selected tool changes.
  useEffect(() => {
    const holder = holderRef.current
    if (!holder) return
    while (holder.children.length) { const child = holder.children[0]; holder.remove(child); child.traverse(o => { if (o instanceof THREE.Mesh) { o.geometry.dispose(); (o.material as THREE.Material).dispose() } }) }
    holder.rotation.set(0, 0, 0)
    holder.add(TOOLS[index].build())
  }, [index])

  return <div ref={stageRef} className="inv-stage" />
}

export function Inventory({ onClose, session, onTearToBoard }: { onClose?: () => void; session?: PhoneSession; onTearToBoard?: (text: string) => void }) {
  const [index, setIndex] = useState(0)
  const [active, setActive] = useState<string | null>(null)

  // Keyboard rack controls: ← / → cycle tools, Space uses the selected one. Inert once a
  // tool is open, so its own inputs (e.g. the notebook textarea) keep the keys.
  useEffect(() => {
    if (active) return
    const onKey = (event: KeyboardEvent) => {
      if (event.key === 'ArrowLeft') { event.preventDefault(); setIndex(i => (i - 1 + TOOLS.length) % TOOLS.length) }
      else if (event.key === 'ArrowRight') { event.preventDefault(); setIndex(i => (i + 1) % TOOLS.length) }
      else if (event.key === ' ') { event.preventDefault(); setActive(TOOLS[index].key) }
    }
    window.addEventListener('keydown', onKey)
    return () => window.removeEventListener('keydown', onKey)
  }, [index, active])

  if (active) return <div className="inv-tool">
    <button className="inv-back" onClick={() => setActive(null)}>‹ TOOLS</button>
    {onClose && <button className="inv-exit" onClick={onClose}>EXIT ✕</button>}
    {active === 'phone' ? <PhonePreview session={session} /> : <NotebookTool playthroughId={session?.playthroughId} onTear={onTearToBoard} />}
  </div>

  return <div className="inv-backdrop">
    <ToolRack index={index} />
    <button className="inv-arrow left" onClick={() => setIndex(i => (i - 1 + TOOLS.length) % TOOLS.length)} aria-label="Previous tool">◀</button>
    <button className="inv-arrow right" onClick={() => setIndex(i => (i + 1) % TOOLS.length)} aria-label="Next tool">▶</button>
    <div className="inv-plate">
      <div className="inv-name">{TOOLS[index].name}</div>
      <div className="inv-dots">{TOOLS.map((tool, i) => <span key={tool.key} className={i === index ? 'on' : ''} />)}</div>
      <button className="inv-use" onClick={() => setActive(TOOLS[index].key)}>USE ▸</button>
    </div>
    {onClose && <button className="inv-close" onClick={onClose}>×</button>}
    <p className="inv-hint">INVENTORY · dev preview</p>
  </div>
}

// Field notebook: the lines captured from NPCs during play. A page can be torn off
// onto the board as a note (when a board tear handler is available).
function NotebookTool({ playthroughId, onTear }: { playthroughId?: string; onTear?: (text: string) => void }) {
  const [pages, setPages] = useState<{ id: string; text: string }[]>([])
  const [draft, setDraft] = useState('')
  useEffect(() => {
    if (!playthroughId) return
    fetch(`/api/playthroughs/${playthroughId}/notebook`).then(r => r.ok ? r.json() : []).then(setPages).catch(() => {})
  }, [playthroughId])
  const remove = async (id: string) => {
    if (playthroughId) await fetch(`/api/playthroughs/${playthroughId}/notebook/${id}`, { method: 'DELETE' })
    setPages(list => list.filter(page => page.id !== id))
  }
  const tear = (page: { id: string; text: string }) => { onTear?.(page.text); void remove(page.id) }
  const add = async () => {
    const text = draft.trim()
    if (!text || !playthroughId) return
    const res = await fetch(`/api/playthroughs/${playthroughId}/notebook`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ text }) })
    if (res.ok) { const page = await res.json(); setPages(list => [...list, { id: page.id, text: page.text }]); setDraft('') }
  }
  return <div className="notebook">
    <div className="notebook-page">
      <div className="notebook-head">FIELD NOTEBOOK</div>
      {playthroughId && <div className="notebook-add">
        <textarea value={draft} onChange={event => setDraft(event.target.value)} placeholder="Write your own note…" spellCheck={false} rows={2} />
        <button disabled={!draft.trim()} onClick={add}>+ Add</button>
      </div>}
      {!pages.length && <p className="notebook-empty">Nothing noted yet. Write one above, or during a conversation use “✎ Note this”.</p>}
      {pages.map(page => <div key={page.id} className="notebook-note">
        <p>{page.text}</p>
        <div className="notebook-note-actions">
          {onTear && <button onClick={() => tear(page)} title="Tear off onto the board">✂ Tear to board</button>}
          <button className="ghost" onClick={() => void remove(page.id)} title="Discard">×</button>
        </div>
      </div>)}
    </div>
  </div>
}
