'use client'

import ProductIcon from '@/components/ProductIcon'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { verifyTicketOffline } from '@/lib/client-verify'

// ─────────────────────────────────────────────────────────────
// Barra offline-first:
//  - Todo lo que hace el bartender se guarda en una cola local (localStorage).
//  - La cola se envía cuando hay red; el servidor la aplica una sola vez.
//  - Los QR se verifican con la clave pública, sin servidor.
// ─────────────────────────────────────────────────────────────

type Item = { name: string; quantity: number; routeTag: string; icon?: string | null }
type Order = { id: string; number: number; status: string; buyerName: string | null; paidAt: string | null; items: Item[] }
type Boot = { station: { id: string; name: string }; event: { id: string; name: string }; publicKey: string; orders: Order[] }
type Action = { id: string; orderId: string; action: 'preparing' | 'ready' | 'delivered'; at: string }
type Flash = { kind: 'ok' | 'err' | 'warn'; text: string } | null

const uuid = () => crypto.randomUUID()
const vibrate = (p: number | number[]) => { try { navigator.vibrate?.(p) } catch {} }
const load = <T,>(k: string, d: T): T => { try { return JSON.parse(localStorage.getItem(k) ?? '') as T } catch { return d } }
const save = (k: string, v: unknown) => { try { localStorage.setItem(k, JSON.stringify(v)) } catch {} }

export default function BarApp({ stationId }: { stationId: string }) {
  const bootKey = `boot:${stationId}`, queueKey = `queue:${stationId}`
  const [boot, setBoot] = useState<Boot | null>(null)
  const [queue, setQueue] = useState<Action[]>([])
  const [online, setOnline] = useState(true)
  const [flash, setFlash] = useState<Flash>(null)
  const [scanning, setScanning] = useState(false)
  const [manual, setManual] = useState('')
  const syncing = useRef(false)
  const queueRef = useRef<Action[]>([])
  queueRef.current = queue

  // Estado inicial desde el celular (funciona sin red)
  useEffect(() => {
    setBoot(load<Boot | null>(bootKey, null))
    setQueue(load<Action[]>(queueKey, []))
    setOnline(navigator.onLine)
    navigator.serviceWorker?.register('/sw.js').catch(() => {})
    const on = () => setOnline(true), off = () => setOnline(false)
    window.addEventListener('online', on); window.addEventListener('offline', off)
    return () => { window.removeEventListener('online', on); window.removeEventListener('offline', off) }
  }, [bootKey, queueKey])

  const refresh = useCallback(async () => {
    try {
      const r = await fetch('/api/estacion/bootstrap', { cache: 'no-store' })
      if (r.status === 401) { window.location.reload(); return }
      if (!r.ok) throw new Error()
      const data: Boot = await r.json()
      setBoot(data); save(bootKey, data); setOnline(true)
    } catch { setOnline(false) }
  }, [bootKey])

  const sync = useCallback(async () => {
    if (syncing.current || queueRef.current.length === 0) return
    syncing.current = true
    const batch = queueRef.current.slice(0, 200)
    try {
      const r = await fetch('/api/estacion/sync', {
        method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ actions: batch }),
      })
      if (!r.ok) throw new Error()
      const { results } = await r.json() as { results: { id: string; outcome: string }[] }
      const done = new Set(results.map(x => x.id))
      const conflicts = results.filter(x => x.outcome === 'conflict').length
      setQueue(q => { const next = q.filter(a => !done.has(a.id)); save(queueKey, next); return next })
      if (conflicts) setFlash({ kind: 'warn', text: `${conflicts} entrega(s) ya registradas por otra barra` })
      setOnline(true)
      await refresh()
    } catch { setOnline(false) }
    finally { syncing.current = false }
  }, [queueKey, refresh])

  // Ciclo: sincronizar cada 4 s, refrescar cada 20 s, y al instante con el stream
  useEffect(() => {
    refresh()
    const a = setInterval(sync, 4000), b = setInterval(refresh, 20000)
    return () => { clearInterval(a); clearInterval(b) }
  }, [refresh, sync])

  useEffect(() => {
    if (!boot?.event.id) return
    let t: ReturnType<typeof setTimeout> | undefined
    const es = new EventSource(`/api/stream/${boot.event.id}`)
    es.onmessage = m => {
      try { if (JSON.parse(m.data).status === 'paid') vibrate(60) } catch {}
      clearTimeout(t); t = setTimeout(refresh, 300)
    }
    es.onerror = () => setOnline(navigator.onLine)
    return () => { clearTimeout(t); es.close() }
  }, [boot?.event.id, refresh])

  // Estado visible = servidor + acciones locales todavía no confirmadas
  const orders = useMemo(() => {
    const last = new Map<string, Action>()
    queue.forEach(a => last.set(a.orderId, a))
    return (boot?.orders ?? [])
      .map(o => (last.has(o.id) ? { ...o, status: last.get(o.id)!.action } : o))
      .filter(o => o.status !== 'delivered')
  }, [boot, queue])

  const act = (o: Order, action: Action['action']) => {
    const a = { id: uuid(), orderId: o.id, action, at: new Date().toISOString() }
    setQueue(q => { const next = [...q, a]; save(queueKey, next); return next })
    vibrate(action === 'delivered' ? [40, 30, 40] : 20)
    if (action === 'delivered') setFlash({ kind: 'ok', text: `#${o.number} entregado` })
    setTimeout(sync, 50)
  }

  const deliverByToken = async (raw: string) => {
    if (!boot) return
    const t = await verifyTicketOffline(raw, boot.publicKey)
    if (!t) { vibrate([300]); setFlash({ kind: 'err', text: 'QR inválido: no es un pase de UghPass' }); return }
    if (t.eventId !== boot.event.id) { vibrate([300]); setFlash({ kind: 'err', text: 'Pase de otro evento' }); return }
    deliverOrder(t.orderId, t.number)
  }

  const deliverOrder = (orderId: string | null, number: number) => {
    const o = orders.find(x => (orderId ? x.id === orderId : x.number === number))
    if (o) return act(o, 'delivered')
    const alreadyDone = queue.some(a => a.action === 'delivered' && (a.orderId === orderId))
    vibrate([300])
    setFlash(alreadyDone
      ? { kind: 'warn', text: `#${number} ya fue entregado` }
      : { kind: 'warn', text: `#${number} no está pago o no corresponde a esta barra` })
  }

  useEffect(() => { if (!flash) return; const t = setTimeout(() => setFlash(null), 3500); return () => clearTimeout(t) }, [flash])

  if (!boot) return <main className="flex min-h-dvh items-center justify-center bg-ink-950 text-gray-400">Cargando estación…</main>

  const cols: { key: string; title: string; list: Order[] }[] = [
    { key: 'paid', title: 'Nuevos', list: orders.filter(o => o.status === 'paid') },
    { key: 'preparing', title: 'Preparando', list: orders.filter(o => o.status === 'preparing') },
    { key: 'ready', title: 'Listos', list: orders.filter(o => o.status === 'ready') },
  ]

  return (
    <main className="flex min-h-dvh flex-col bg-ink-950 text-white">
      <header className="flex items-center justify-between gap-3 border-b border-ink-800 px-4 py-3">
        <div className="min-w-0">
          <p className="truncate text-xs text-gray-500">{boot.event.name}</p>
          <h1 className="truncate text-lg font-semibold">{boot.station.name}</h1>
        </div>
        <div className="flex items-center gap-2">
          <span className={`chip ${online ? 'bg-live/15 text-green-300' : 'bg-amber-500/20 text-amber-200'}`}>
            {online ? 'En línea' : 'Sin conexión'}{queue.length > 0 && ` · ${queue.length} por enviar`}
          </span>
          <button onClick={() => setScanning(true)} className="btn-brand py-2.5">Escanear</button>
        </div>
      </header>

      {flash && (
        <div className={`mx-4 mt-3 animate-scale-in rounded-control px-4 py-3 text-center text-lg font-semibold ${
          flash.kind === 'ok' ? 'bg-live text-ink-950' : flash.kind === 'err' ? 'bg-red-500 text-white' : 'bg-amber-400 text-ink-950'}`}>
          {flash.text}
        </div>
      )}

      <form onSubmit={e => { e.preventDefault(); const n = Number(manual); if (n) deliverOrder(null, n); setManual('') }}
        className="flex gap-2 px-4 pt-3">
        <input value={manual} onChange={e => setManual(e.target.value.replace(/\D/g, ''))} inputMode="numeric"
          placeholder="Entregar por número" className="flex-1 rounded-control border border-ink-700 bg-ink-900 px-4 py-3 outline-none focus:border-brand-400" />
        <button className="btn bg-ink-700 px-5 text-white">Entregar</button>
      </form>

      <div className="grid flex-1 gap-3 p-4 md:grid-cols-3">
        {cols.map(col => (
          <section key={col.key} className="flex min-h-0 flex-col rounded-card bg-ink-900">
            <h2 className="flex items-center justify-between px-4 py-3 text-sm font-semibold uppercase tracking-wide text-gray-400">
              {col.title}<span className="rounded-full bg-ink-700 px-2.5 py-0.5 text-white">{col.list.length}</span>
            </h2>
            <div className="flex-1 space-y-2 overflow-y-auto px-3 pb-3">
              {col.list.map(o => <OrderCard key={o.id} order={o} onAct={act} />)}
            </div>
          </section>
        ))}
      </div>

      {scanning && <Scanner onClose={() => setScanning(false)} onCode={c => { setScanning(false); deliverByToken(c) }} />}
    </main>
  )
}

function OrderCard({ order, onAct }: { order: Order; onAct: (o: Order, a: Action['action']) => void }) {
  const mins = order.paidAt ? Math.floor((Date.now() - new Date(order.paidAt).getTime()) / 60000) : 0
  return (
    <article className="animate-fade-up rounded-control bg-ink-800 p-3">
      <div className="mb-2 flex items-baseline justify-between">
        <span className="text-3xl font-bold tabular-nums">#{order.number}</span>
        <span className={`text-xs ${mins >= 10 ? 'text-red-400' : 'text-gray-500'}`}>{mins} min</span>
      </div>
      {order.buyerName && <p className="mb-1 text-sm text-brand-200">{order.buyerName}</p>}
      <ul className="mb-3 space-y-0.5 text-[15px]">
        {order.items.map((i, k) => <li key={k}><b className="tabular-nums">{i.quantity}×</b> {i.icon && <span className="mr-1 inline-flex h-6 w-6 items-center justify-center rounded-md bg-ink-800 align-middle"><ProductIcon icon={i.icon} size="88%" /></span>}{i.name}</li>)}
      </ul>
      <div className="flex gap-2">
        {order.status === 'paid' && <button onClick={() => onAct(order, 'preparing')} className="btn flex-1 bg-ink-600 py-2.5 text-white">Preparar</button>}
        {order.status !== 'ready' && <button onClick={() => onAct(order, 'ready')} className="btn flex-1 bg-brand-500 py-2.5 font-bold text-ink-950">Listo</button>}
        {order.status === 'ready' && <button onClick={() => onAct(order, 'delivered')} className="btn flex-1 bg-live py-2.5 text-ink-950">Entregar</button>}
      </div>
    </article>
  )
}

// Escáner con la API nativa BarcodeDetector (Chrome/Android). Si no existe, se usa el número.
function Scanner({ onClose, onCode }: { onClose: () => void; onCode: (c: string) => void }) {
  const video = useRef<HTMLVideoElement>(null)
  const [error, setError] = useState<string | null>(null)

  useEffect(() => {
    let stream: MediaStream | null = null, raf = 0, stopped = false
    const Detector = (window as unknown as { BarcodeDetector?: new (o: { formats: string[] }) => { detect: (v: HTMLVideoElement) => Promise<{ rawValue: string }[]> } }).BarcodeDetector
    if (!Detector) { setError('Este navegador no puede escanear. Usá "Entregar por número".'); return }
    const detector = new Detector({ formats: ['qr_code'] })
    navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' } }).then(s => {
      stream = s
      if (!video.current) return
      video.current.srcObject = s
      video.current.play()
      const tick = async () => {
        if (stopped || !video.current) return
        try {
          const codes = await detector.detect(video.current)
          if (codes[0]?.rawValue) { onCode(codes[0].rawValue); return }
        } catch {}
        raf = requestAnimationFrame(tick)
      }
      tick()
    }).catch(() => setError('No hay permiso para usar la cámara.'))
    return () => { stopped = true; cancelAnimationFrame(raf); stream?.getTracks().forEach(t => t.stop()) }
  }, [onCode])

  return (
    <div className="fixed inset-0 z-50 flex flex-col bg-black">
      <video ref={video} playsInline muted className="flex-1 object-cover" />
      <div className="pointer-events-none absolute inset-0 flex items-center justify-center">
        <div className="h-64 w-64 rounded-3xl border-4 border-white/80" />
      </div>
      {error && <p className="absolute inset-x-6 top-10 rounded-control bg-amber-400 p-3 text-center font-semibold text-ink-950">{error}</p>}
      <button onClick={onClose} className="btn safe-bottom m-4 bg-white py-4 text-ink-950">Cerrar</button>
    </div>
  )
}
