// UI kit — reusable pixel components. Ported from prototype/js/ui.jsx, wired to the
// store + procedural art modules instead of globals.
import type { ComponentChildren, JSX } from 'preact'
import { useEffect, useState } from 'preact/hooks'

import { EV_ICONS, IPAL, bodyFor, portraitFor, sceneFor } from '../engine/art'
import { PixelCanvas, SceneCanvas, useTypewriter } from '../engine/pixel'
import { useGame } from '../store'
import type { Evidence, Suspect } from '../types'
import { type Sfx, musicIsPlaying, playSfx, toggleMusic } from './audio'
import { TweaksSheet } from './tweaks'

const pxScaled = (px: number, floor = 2) =>
  Math.max(floor, Math.round(px * (window.__pxScale || 1)))

// ---- sprite wrappers ----
export function useBlink(): boolean {
  const [b, setB] = useState(false)
  useEffect(() => {
    let t1: ReturnType<typeof setTimeout>
    let t2: ReturnType<typeof setTimeout>
    let alive = true
    const loop = () => {
      const wait = 2400 + Math.random() * 3200
      t1 = setTimeout(() => {
        if (!alive) return
        setB(true)
        t2 = setTimeout(() => {
          if (alive) {
            setB(false)
            loop()
          }
        }, 120)
      }, wait)
    }
    loop()
    return () => {
      alive = false
      clearTimeout(t1)
      clearTimeout(t2)
    }
  }, [])
  return b
}

interface SpriteProps {
  id: string
  px?: number
  style?: JSX.CSSProperties
  className?: string
}
// Toggle the mouth open/closed at a speech-like cadence while the suspect's voice plays.
function useMouth(active: boolean): boolean {
  const [open, setOpen] = useState(false)
  useEffect(() => {
    if (!active) {
      setOpen(false)
      return
    }
    const id = setInterval(() => setOpen((o) => !o), 135)
    return () => clearInterval(id)
  }, [active])
  return open
}
export function Portrait({ id, px = 6, blink = true, talking = false, style, className, gender }: SpriteProps & { blink?: boolean; talking?: boolean; gender?: string }) {
  const p = portraitFor(id, gender)
  const b = useBlink()
  const mouth = useMouth(talking)
  let frame = p.frames[0]
  if (talking && p.frames[2]) frame = mouth ? p.frames[2] : p.frames[0]
  else if (blink && b) frame = p.frames[1]
  return <PixelCanvas map={frame} pal={p.pal} px={pxScaled(px)} style={style} className={className} />
}
export function Body({ id, px = 6, playing = true, style, className, gender }: SpriteProps & { playing?: boolean; gender?: string }) {
  const p = bodyFor(id, gender)
  return <PixelCanvas frames={p.frames} pal={p.pal} px={pxScaled(px)} fps={1.1} playing={playing} style={style} className={className} />
}
export function EvIcon({ icon, px = 4, style }: { icon: string; px?: number; style?: JSX.CSSProperties }) {
  const m = EV_ICONS[icon] || EV_ICONS.photoEv
  return <PixelCanvas map={m} pal={IPAL} px={pxScaled(px, 1)} style={style} />
}
interface SceneProps {
  name: string
  w?: number
  h?: number
  anim?: boolean
  full?: boolean
  cover?: boolean
  style?: JSX.CSSProperties
  className?: string
  deps?: unknown[]
}
export function Scene({ name, w = 240, h = 135, anim = false, full = false, cover = false, style, className, deps = [] }: SceneProps) {
  const st = cover ? { objectFit: 'cover' as const, ...style } : style
  return (
    <SceneCanvas
      paint={sceneFor(name)}
      w={w}
      h={h}
      anim={anim}
      full={full}
      style={st}
      className={className}
      deps={[name, anim, full, ...deps]}
    />
  )
}

// ---- primitives ----
interface PanelProps {
  tab?: string
  variant?: 'amber' | 'ox' | 'raised' | 'inset'
  className?: string
  style?: JSX.CSSProperties
  children?: ComponentChildren
  onClick?: (e: MouseEvent) => void
}
export function Panel({ tab, variant, className = '', style, children, onClick }: PanelProps) {
  const v = variant ? ` panel--${variant}` : ''
  return (
    <div class={`panel${v} ${className}`} style={style} onClick={onClick}>
      {tab && <span class="panel__tab">{tab}</span>}
      {children}
    </div>
  )
}

type BtnProps = {
  variant?: 'amber' | 'ox' | 'ghost'
  sm?: boolean
  className?: string
  children?: ComponentChildren
  /** SFX on click: a specific cue, or null to silence. Defaults to "click". */
  sfx?: Sfx | null
} & JSX.HTMLAttributes<HTMLButtonElement>
export function Btn({ variant, sm, className = '', children, sfx, onClick, ...rest }: BtnProps) {
  const v = variant ? ` pbtn--${variant}` : ''
  const handle = (e: MouseEvent) => {
    if (sfx !== null) playSfx(sfx || 'click')
    ;(onClick as ((ev: MouseEvent) => void) | undefined)?.(e)
  }
  return (
    <button class={`pbtn${v}${sm ? ' pbtn--sm' : ''} ${className}`} onClick={handle} {...rest}>
      {children}
    </button>
  )
}

export function Chip({ variant, children, style }: { variant?: 'amber' | 'ox'; children?: ComponentChildren; style?: JSX.CSSProperties }) {
  const v = variant ? ` chip--${variant}` : ''
  return <span class={`chip${v}`} style={style}>{children}</span>
}

export function Stamp({ children, slam, style }: { children?: ComponentChildren; slam?: boolean; style?: JSX.CSSProperties }) {
  return <span class={`stamp${slam ? ' stamp--slam' : ''}`} style={style}>{children}</span>
}

// ---- suspicion / composure bar ----
export function SuspicionBar({ value, label = 'SUSPICION', compact }: { value: number; label?: string | null; compact?: boolean }) {
  const v = Math.max(0, Math.min(100, Math.round(value)))
  const calm = v < 42
  return (
    <div class="col" style={{ gap: compact ? 3 : 5 }}>
      {!compact && label && (
        <div class="between">
          <span class="t-label">{label}</span>
          <span class="t-mono" style={{ color: calm ? 'var(--slate-3)' : 'var(--ox-3)', fontSize: 'calc(14px*var(--mono-scale))' }}>{v}%</span>
        </div>
      )}
      <div class="bar" style={compact ? { height: 10 } : undefined}>
        <div class={'bar__fill' + (calm ? ' bar__fill--calm' : '')} style={{ width: v + '%' }} />
      </div>
    </div>
  )
}

// ---- suspect card ----
export function SuspectCard({ s, onClick, active, mini }: { s: Suspect; onClick?: (e: MouseEvent) => void; active?: boolean; mini?: boolean }) {
  const g = useGame()
  const susp = g.state.suspicion[s.id]
  return (
    <Panel
      className="suspect-card"
      variant={active ? 'amber' : undefined}
      style={{ padding: mini ? 8 : 12, cursor: onClick ? 'pointer' : 'default', minWidth: mini ? 0 : 168 }}
      onClick={onClick}
    >
      <div class="row" style={{ gap: 10, alignItems: 'flex-start' }}>
        <div style={{ background: 'var(--ink-1)', boxShadow: 'inset 0 0 0 2px var(--ink-0)', padding: 3, flexShrink: 0 }}>
          <Portrait id={s.sprite} px={mini ? 3 : 4} gender={s.gender} />
        </div>
        <div class="col grow" style={{ gap: 5, minWidth: 0 }}>
          <div>
            <div class="t-display" style={{ fontSize: mini ? 9 : 11, color: 'var(--bone-3)' }}>{s.name}</div>
            <div class="t-label" style={{ marginTop: 3 }}>{s.tag}</div>
          </div>
          {!mini && <div class="t-body dim" style={{ fontSize: 12, lineHeight: 1.35 }}>{s.role}</div>}
          <SuspicionBar value={susp} compact />
        </div>
      </div>
    </Panel>
  )
}

// ---- evidence card with "develops in" reveal ----
export function EvidenceCard({ e, onClick, active, develop, small }: { e: Evidence; onClick?: (ev: MouseEvent) => void; active?: boolean; develop?: boolean; small?: boolean }) {
  const [revealed, setRevealed] = useState(!develop)
  useEffect(() => {
    if (develop) {
      const t = setTimeout(() => setRevealed(true), 60)
      return () => clearTimeout(t)
    }
  }, [develop])
  return (
    <Panel
      variant={active ? 'amber' : undefined}
      className="ev-card"
      style={{ padding: small ? 8 : 11, cursor: onClick ? 'pointer' : 'default', position: 'relative', overflow: 'hidden' }}
      onClick={onClick}
    >
      <div class="row" style={{ gap: 10, alignItems: 'center' }}>
        <div style={{ background: 'var(--ink-1)', boxShadow: 'inset 0 0 0 2px var(--ink-0)', padding: 4, flexShrink: 0 }}>
          <EvIcon icon={e.icon} px={small ? 2 : 3} />
        </div>
        <div class="col grow" style={{ gap: 4, minWidth: 0 }}>
          <div class="t-display" style={{ fontSize: small ? 8 : 10, color: 'var(--bone-3)' }}>{e.name}</div>
          <div class="row" style={{ gap: 6, alignItems: 'center' }}>
            <Chip variant="amber" style={{ fontSize: 'calc(12px*var(--mono-scale))', padding: '2px 5px' }}>{e.type}</Chip>
            <span class="t-mono dim nowrap" style={{ fontSize: 'calc(13px*var(--mono-scale))' }}>{e.time}</span>
          </div>
        </div>
      </div>
      {develop && !revealed && <span class="develop-veil" />}
      {develop && <span class="develop-veil develop-veil--anim" />}
    </Panel>
  )
}

// ---- dialogue / speech panel with typewriter ----
export function DialoguePanel({ who, text, speed = 26, onDone, instant, tag }: { who: string; text: string; speed?: number; onDone?: () => void; instant?: boolean; tag?: string | null }) {
  const [out, done] = useTypewriter(text, speed, !instant)
  useEffect(() => {
    if (done && onDone) onDone()
  }, [done])
  return (
    <Panel className="dialogue" style={{ padding: 16 }}>
      <div class="between" style={{ marginBottom: 8 }}>
        <span class="t-display amber" style={{ fontSize: 11 }}>{who}</span>
        {tag && <Chip variant="ox">{tag}</Chip>}
      </div>
      <div class="t-body" style={{ minHeight: 56, color: 'var(--bone-2)' }}>
        {out}
        {!done && <span class="cursor" />}
      </div>
    </Panel>
  )
}

// ---- type a string once, fire onDone ----
export function TypeOnce({ text, speed, onDone }: { text: string; speed: number; onDone?: () => void }) {
  const [out, done] = useTypewriter(text, speed, true)
  const [fired, setFired] = useState(false)
  useEffect(() => {
    if (done && !fired) {
      setFired(true)
      onDone?.()
    }
  }, [done])
  return (
    <>
      {out}
      {!done && <span class="cursor" />}
    </>
  )
}

// ---- HINT trigger button ----
export function HintButton() {
  return (
    <button class="hint-btn" onClick={() => window.dispatchEvent(new Event('toggle-hint'))} title="Ask your partner">
      <span class="hint-btn__dot" /> HINT
    </button>
  )
}

// ---- top HUD bar ----
// Navbar controls: optional Main-menu, Music toggle, Settings sheet. Compact icon buttons so
// they sit gracefully in the HUD bar (and on mobile) instead of floating mid-screen.
export function Controls({ menu = false }: { menu?: boolean }) {
  const g = useGame()
  const [musicOn, setMusicOn] = useState(musicIsPlaying())
  const [settings, setSettings] = useState(false)
  const icon = (label: string, title: string, on: boolean, onClick: () => void) => (
    <button
      class="hint-btn"
      title={title}
      aria-label={title}
      onClick={onClick}
      style={{ width: 30, height: 28, padding: 0, justifyContent: 'center', fontSize: '0.95rem', opacity: on ? 1 : 0.5 }}
    >
      {label}
    </button>
  )
  return (
    <div class="row" style={{ gap: 5, alignItems: 'center' }}>
      {menu && <Btn sm variant="ghost" onClick={() => g.nav('title')}>Menu</Btn>}
      {icon('♪', musicOn ? 'Music: on' : 'Music: off', musicOn, () => setMusicOn(toggleMusic()))}
      {icon('⚙', 'Settings', true, () => setSettings(true))}
      {settings && <TweaksSheet onClose={() => setSettings(false)} />}
    </div>
  )
}

export function Hud({ title, sub, right }: { title: string; sub?: string; right?: ComponentChildren }) {
  const g = useGame()
  return (
    <div class="hud">
      <div class="row" style={{ gap: 12, alignItems: 'center', minWidth: 0 }}>
        <button class="hud-badge" onClick={() => g.nav('board')} title="Investigation Board">
          <span class="t-display" style={{ fontSize: 9, color: 'var(--ink-0)' }}>CASE</span>
          <span class="t-mono" style={{ fontSize: 'calc(13px*var(--mono-scale))', color: 'var(--ink-0)' }}>{g.case.id}</span>
        </button>
        <div class="col" style={{ gap: 2, minWidth: 0 }}>
          <div class="t-display hud__title">{title}</div>
          {sub && <div class="t-label nowrap">{sub}</div>}
        </div>
      </div>
      <div class="row" style={{ gap: 6, alignItems: 'center' }}>
        {right}
        <Controls menu />
        <HintButton />
      </div>
    </div>
  )
}

// ---- bottom nav (mobile) ----
const NAV_ITEMS = [
  { id: 'briefing', label: 'CASE', icon: 'file' },
  { id: 'board', label: 'BOARD', icon: 'board' },
  { id: 'suspects', label: 'SUSPECTS', icon: 'people' },
  { id: 'evidence', label: 'EVIDENCE', icon: 'box' },
  { id: 'timeline', label: 'TIME', icon: 'clock' },
] as const

function NavGlyph({ icon, on }: { icon: string; on: boolean }) {
  const c = on ? 'var(--ink-0)' : 'var(--bone-1)'
  const P = (d: JSX.CSSProperties) => <span style={{ position: 'absolute', ...d }} />
  return (
    <span style={{ position: 'relative', width: 18, height: 18, display: 'inline-block' }}>
      {icon === 'file' && (<>{P({ left: 4, top: 2, width: 10, height: 14, background: 'transparent', boxShadow: `inset 0 0 0 2px ${c}` })}{P({ left: 6, top: 6, width: 6, height: 2, background: c })}{P({ left: 6, top: 10, width: 6, height: 2, background: c })}</>)}
      {icon === 'board' && (<>{P({ left: 2, top: 3, width: 14, height: 12, background: 'transparent', boxShadow: `inset 0 0 0 2px ${c}` })}{P({ left: 6, top: 7, width: 3, height: 3, background: c })}{P({ left: 11, top: 9, width: 3, height: 3, background: c })}</>)}
      {icon === 'people' && (<>{P({ left: 3, top: 3, width: 5, height: 5, background: c })}{P({ left: 3, top: 9, width: 7, height: 6, background: c })}{P({ left: 11, top: 4, width: 4, height: 4, background: c })}{P({ left: 10, top: 9, width: 6, height: 6, background: c })}</>)}
      {icon === 'box' && (<>{P({ left: 3, top: 4, width: 12, height: 11, background: 'transparent', boxShadow: `inset 0 0 0 2px ${c}` })}{P({ left: 3, top: 4, width: 12, height: 2, background: c })}{P({ left: 8, top: 8, width: 2, height: 4, background: c })}</>)}
      {icon === 'clock' && (<>{P({ left: 3, top: 3, width: 12, height: 12, background: 'transparent', boxShadow: `inset 0 0 0 2px ${c}` })}{P({ left: 8, top: 6, width: 2, height: 4, background: c })}{P({ left: 8, top: 9, width: 4, height: 2, background: c })}</>)}
    </span>
  )
}

export function BottomNav() {
  const g = useGame()
  const cur = g.state.screen
  return (
    <nav class="bottom-nav">
      {NAV_ITEMS.map((it) => {
        const on = cur === it.id || (it.id === 'suspects' && cur === 'interro')
        return (
          <button
            key={it.id}
            class={'nav-btn' + (on ? ' nav-btn--on' : '')}
            onClick={() => g.nav((it.id === 'suspects' ? 'board' : it.id) as Parameters<typeof g.nav>[0])}
          >
            <NavGlyph icon={it.icon} on={on} />
            <span class="t-display" style={{ fontSize: 7, letterSpacing: '.06em' }}>{it.label}</span>
          </button>
        )
      })}
    </nav>
  )
}
