import { createContext, useContext, useCallback, useEffect, useRef } from 'react'
import useAuthStore from '../store/authStore'
import useConnectionStore from '../store/connectionStore'
import { useSSE } from '../hooks/useSSE'
import db from '../db/posdb'
import client from '../api/client'
import { flushOfflinePayments } from '../services/offlinePayments'

const SSEContext = createContext(null)

export function useSSEContext() {
  return useContext(SSEContext)
}

const HEARTBEAT_INTERVAL = 30_000

export function SSEProvider({ children }) {
  const { token } = useAuthStore()
  const { setLost, setOnline } = useConnectionStore()
  const sseAlive = useRef(false)
  const heartbeatRef = useRef(null)

  // Keep setLost/setOnline in refs so heartbeat/event closures are never stale
  const setLostRef = useRef(setLost)
  const setOnlineRef = useRef(setOnline)
  useEffect(() => { setLostRef.current = setLost }, [setLost])
  useEffect(() => { setOnlineRef.current = setOnline }, [setOnline])

  // ── Snapshot helpers ─────────────────────────────────────────────────────────

  const snapshotTables = useCallback(async () => {
    try {
      const res = await client.get('/api/tables/')
      await db.tables.bulkPut(res.data)
    } catch { /* offline — snapshot stays as-is */ }
  }, [])

  const snapshotOrders = useCallback(async () => {
    try {
      const res = await client.get('/api/orders/active')
      const slimOrders = res.data
      // Fetch full order details (with items) so emergency mode has them
      const fullOrders = await Promise.all(
        slimOrders.map(o =>
          client.get(`/api/orders/${o.id}`)
            .then(r => ({
              ...r.data,
              waiter_ids: r.data.waiters?.map(w => w.waiter_id) ?? o.waiter_ids ?? [],
            }))
            .catch(() => o)
        )
      )
      await db.orders.bulkPut(fullOrders)
    } catch { /* offline — snapshot stays as-is */ }
  }, [])

  const fullRefresh = useCallback(async () => {
    await Promise.all([snapshotTables(), snapshotOrders()])
  }, [snapshotTables, snapshotOrders])

  // ── SSE event handler ────────────────────────────────────────────────────────

  const handleEvent = useCallback(async (type, data) => {
    // Dispatch for any UI component listening to window events
    window.dispatchEvent(new CustomEvent('sse-event', { detail: { type, data } }))

    // Incrementally update IndexedDB snapshot
    switch (type) {
      case 'order_updated':
      case 'order_paid': {
        // Try to fetch the full order to keep items in the snapshot
        try {
          const full = await client.get(`/api/orders/${data.order_id}`)
          const o = full.data
          await db.orders.put({
            ...o,
            waiter_ids: o.waiters?.map(w => w.waiter_id) ?? [],
          })
        } catch {
          // Fallback: update only the slim fields we know
          const existing = await db.orders.get(data.order_id)
          await db.orders.put({
            ...(existing || {}),
            id: data.order_id,
            table_id: data.table_id,
            status: data.status,
            waiter_ids: existing?.waiter_ids || [],
          })
        }
        break
      }
      case 'order_closed': {
        await db.orders.delete(data.order_id)
        break
      }
      case 'table_list_changed': {
        await snapshotTables()
        break
      }
      default:
        break
    }
  }, [snapshotTables])

  // ── SSE connection lifecycle ─────────────────────────────────────────────────

  const handleConnect = useCallback(async () => {
    sseAlive.current = true
    const wasEmergency = useConnectionStore.getState().status === 'emergency'
    setOnlineRef.current()
    window.dispatchEvent(new Event('sse-reconnected'))
    if (wasEmergency) {
      const result = await flushOfflinePayments()
      if (result.duplicates > 0 || result.failed > 0) {
        window.dispatchEvent(new CustomEvent('offline-sync-result', { detail: result }))
      }
    }
    await fullRefresh()
  }, [fullRefresh])

  const handleDisconnect = useCallback(() => {
    sseAlive.current = false
    // Don't immediately setLost — heartbeat is the authoritative check
  }, [])

  const { reconnect } = useSSE({
    token,
    enabled: !!token,
    onEvent: handleEvent,
    onConnect: handleConnect,
    onDisconnect: handleDisconnect,
  })

  // ── Heartbeat ────────────────────────────────────────────────────────────────

  useEffect(() => {
    if (!token) return

    async function beat() {
      try {
        await client.get('/api/system/health')
        const currentStatus = useConnectionStore.getState().status
        if (currentStatus === 'lost' || currentStatus === 'emergency') {
          if (currentStatus === 'emergency') {
            const result = await flushOfflinePayments()
            if (result.duplicates > 0 || result.failed > 0) {
              window.dispatchEvent(new CustomEvent('offline-sync-result', { detail: result }))
            }
          }
          setOnlineRef.current()
          reconnect()
          await fullRefresh()
        }
      } catch {
        if (!sseAlive.current) {
          setLostRef.current()
        }
      }
    }

    heartbeatRef.current = setInterval(beat, HEARTBEAT_INTERVAL)
    return () => clearInterval(heartbeatRef.current)
    // reconnect and fullRefresh are stable (useCallback with no changing deps)
  }, [token, reconnect, fullRefresh])

  // ── React to failed API requests (immediate detection) ───────────────────────

  useEffect(() => {
    function onBackendOffline() {
      if (!sseAlive.current) {
        setLostRef.current()
      }
    }
    window.addEventListener('backend-offline', onBackendOffline)
    return () => window.removeEventListener('backend-offline', onBackendOffline)
  }, [])

  // ── Initial snapshot on login ─────────────────────────────────────────────────

  useEffect(() => {
    if (token) fullRefresh()
  }, [token, fullRefresh])

  return (
    <SSEContext.Provider value={{ reconnect, fullRefresh }}>
      {children}
    </SSEContext.Provider>
  )
}
