// Interrogation room — questions, free-text, and present-evidence all go to the server,
// which returns the suspect's reply + the authoritative suspicion. The client only displays.
import { useEffect, useRef, useState } from 'preact/hooks'

import { interrogate } from '../api'
import { HOT_SUSPICION, useGame } from '../store'
import type { Evidence, SuggestedQuestion } from '../types'
import { playSfx, prepareSpeak, stopSpeak } from '../ui/audio'
import { Btn, EvIcon, EvidenceCard, Hud, Panel, Portrait, Scene, SuspicionBar, TypeOnce } from '../ui/components'
import { CrackBanner, DeltaFloater } from '../ui/juice'

interface Pending {
  text: string
  tag?: string | null
  suspicion: number
  speed?: number // typewriter ms/char, paced to the voice when one is available
}

export function Interrogation() {
  const g = useGame()
  const c = g.case
  const sid = (g.state.payload.suspect as string) || c.suspects[0].id
  const s = c.suspects.find((x) => x.id === sid)!
  const transcript = g.state.interrogations[sid] || []
  const susp = g.state.suspicion[sid]
  const usedQ = g.state.usedQ[sid] || []
  const usedEv = g.state.usedEv[sid] || []
  const [pending, setPending] = useState<Pending | null>(null)
  const [thinking, setThinking] = useState(false)
  const [talking, setTalking] = useState(false)
  const [tray, setTray] = useState(false)
  const [input, setInput] = useState('')
  const [floater, setFloater] = useState<{ delta: number; id: number } | null>(null)
  const [crack, setCrack] = useState(false)
  const scroller = useRef<HTMLDivElement>(null)
  const busy = thinking || !!pending

  useEffect(() => () => stopSpeak(), []) // stop any voice when leaving the room

  useEffect(() => {
    if (!transcript.length) setPending({ text: s.greet, suspicion: susp })
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [sid])
  useEffect(() => {
    if (scroller.current) scroller.current.scrollTop = scroller.current.scrollHeight
  }, [transcript, pending, thinking])

  const commit = () => {
    if (!pending) return
    g.dispatch({ type: 'ADD_LINE', sid, line: { role: 'sus', text: pending.text } })
    g.dispatch({ type: 'SUSP_SET', sid, value: pending.suspicion })
    setPending(null)
  }

  const run = async (body: Parameters<typeof interrogate>[2], detLine: { text: string; ev?: string }) => {
    if (busy) return
    stopSpeak()
    setTalking(false)
    g.dispatch({ type: 'ADD_LINE', sid, line: { role: 'det', text: detLine.text, ev: detLine.ev } })
    setThinking(true)
    const fallback = g.state.tweaks.typeSpeed || 22
    try {
      const r = await interrogate(g.runId, sid, body)
      const tag = r.flags?.rattled ? 'RATTLED' : r.flags?.cornered ? 'CRACKING' : null
      // Feedback moments: record what rattled whom, float the suspicion delta, and
      // slam the banner the FIRST time this suspect's lie cracks.
      const flags = r.flags || {}
      const crackedNow = !!(flags.cornered || flags.contradictionExposed)
      const firstCrack = crackedNow && !g.state.cracked[sid]
      if (crackedNow || (flags.rattled && body.presentEvidenceId)) {
        g.dispatch({
          type: 'FLAG',
          sid,
          evId: body.presentEvidenceId,
          kind: flags.contradictionExposed ? 'contradiction' : flags.cornered ? 'cornered' : 'rattled',
        })
      }
      if (typeof r.suspicionDelta === 'number' && r.suspicionDelta !== 0) setFloater({ delta: r.suspicionDelta, id: Date.now() })
      if (firstCrack) {
        setCrack(true)
        playSfx('success')
        setTimeout(() => setCrack(false), 1700)
      }
      // Synthesize the voice during the "thinking" beat so text + speech start together.
      const voice = await prepareSpeak(g.runId, sid, r.reply, () => setTalking(false))
      // Pace the typewriter to the audio so words land in step with the spoken line.
      const speed = voice ? Math.max(14, Math.min(90, Math.round(voice.durationMs / Math.max(1, r.reply.length)))) : fallback
      setThinking(false)
      setPending({ text: r.reply, suspicion: r.suspicion, tag, speed })
      if (voice) {
        setTalking(true)
        voice.play()
      }
    } catch {
      setThinking(false)
      setPending({ text: '…I have nothing more to say to that.', suspicion: susp, speed: fallback })
    }
  }

  const ask = (q: SuggestedQuestion) => {
    if (busy) return
    playSfx('select')
    g.dispatch({ type: 'USEQ', sid, qid: q.id })
    run({ questionId: q.id }, { text: q.q })
  }
  const present = (e: Evidence) => {
    setTray(false)
    if (busy) return
    playSfx('present')
    g.dispatch({ type: 'USEEV', sid, ev: e.id })
    run({ presentEvidenceId: e.id }, { text: `Look at this. ${e.name}.`, ev: e.icon })
  }
  const sendFree = () => {
    const txt = input.trim()
    if (!txt || busy) return
    setInput('')
    run({ freeText: txt }, { text: txt })
  }

  const px = g.mode === 'mobile' ? 8 : 13
  return (
    <div class="app__view">
      <Hud
        title={s.name}
        sub={s.role}
        right={
          <>
            <div class="col" style={{ width: g.mode === 'mobile' ? 96 : 180, position: 'relative' }}>
              <SuspicionBar value={susp} />
              {floater && <DeltaFloater key={floater.id} delta={floater.delta} />}
              {susp >= HOT_SUSPICION && (
                <button class="accuse-chip" onClick={() => g.nav('accuse', { suspect: sid })}>CORNERED — ACCUSE ▸</button>
              )}
            </div>
            <Btn sm variant="ghost" onClick={() => g.nav('board')}>Board</Btn>
          </>
        }
      />
      <div class="interro" style={{ position: 'relative' }}>
        <div class="interro__stage">
          <Scene name="interro" w={220} h={380} style={{ position: 'absolute', inset: 0, width: '100%', height: '100%' }} />
          <div style={{ position: 'absolute', inset: 0, background: 'radial-gradient(55% 45% at 50% 58%, rgba(224,164,76,.12), transparent 72%)' }} />
          <div class="interro__sprite" style={{ bottom: '7%' }}><Portrait id={s.sprite} px={px} gender={s.gender} accent={s.accentColor} talking={talking} /></div>
          {crack && <CrackBanner />}
          <div style={{ position: 'absolute', top: 12, left: 12, right: 12, zIndex: 4 }}>
            <Panel style={{ padding: 8, width: 'fit-content', maxWidth: '100%' }}>
              <span class="t-label">{s.tag}</span>
              <div class="t-mono dim" style={{ fontSize: 'calc(13px*var(--mono-scale))' }}>ALIBI · {s.alibi}</div>
            </Panel>
          </div>
        </div>

        <div class="interro__right">
          <div class="transcript" ref={scroller}>
            {transcript.map((l, i) => (
              <div key={i} class={'tline tline--' + (l.role === 'det' ? 'det' : 'sus')}>
                <div class="tline__who">{l.role === 'det' ? 'YOU' : s.name}</div>
                <div class="tline__b">
                  {l.ev && (
                    <span style={{ display: 'inline-block', verticalAlign: 'middle', marginRight: 6, background: 'var(--ink-1)', padding: 2 }}>
                      <EvIcon icon={l.ev} px={2} />
                    </span>
                  )}
                  {l.text}
                </div>
              </div>
            ))}
            {thinking && (
              <div class="tline tline--sus">
                <div class="tline__who">{s.name}</div>
                <div class="tline__b"><span class="cursor" /></div>
              </div>
            )}
            {pending && (
              <div class="tline tline--sus">
                <div class="tline__who">{s.name}{pending.tag && <span class="ox"> · {pending.tag}</span>}</div>
                <div class="tline__b"><TypeOnce text={pending.text} speed={pending.speed ?? (g.state.tweaks.typeSpeed || 22)} onDone={commit} /></div>
              </div>
            )}
          </div>

          <div class="composer">
            <div class="qsuggest">
              {s.suggestedQuestions.map((q) => (
                <button key={q.id} class="qchip" disabled={usedQ.includes(q.id) || busy} onClick={() => ask(q)}>
                  {usedQ.includes(q.id) ? '✓ ' : '? '}
                  {q.q}
                </button>
              ))}
            </div>
            <div class="composer__input">
              <input
                class="pinput"
                placeholder="Type your question…"
                value={input}
                onInput={(e) => setInput((e.target as HTMLInputElement).value)}
                onKeyDown={(e) => e.key === 'Enter' && sendFree()}
              />
              <Btn variant="amber" sm onClick={sendFree} disabled={busy}>Ask</Btn>
              <Btn variant="ox" sm onClick={() => setTray(true)} disabled={busy}>Evidence ▴</Btn>
            </div>
          </div>
        </div>

        {tray && (
          <div class="tray" onClick={() => setTray(false)}>
            <div class="tray__sheet" onClick={(e) => e.stopPropagation()}>
              <div class="between" style={{ marginBottom: 12 }}>
                <span class="t-display amber" style={{ fontSize: 12 }}>PRESENT EVIDENCE</span>
                <Btn sm variant="ghost" onClick={() => setTray(false)}>✕ Close</Btn>
              </div>
              <div class="tray__grid">
                {c.evidence.map((e) => (
                  <EvidenceCard key={e.id} e={e} active={usedEv.includes(e.id)} onClick={() => present(e)} />
                ))}
              </div>
            </div>
          </div>
        )}
      </div>
    </div>
  )
}
