import { useEffect, useRef, useCallback } from 'react'

type SSEHandler = (data: unknown) => void

interface UseSSEOptions {
  /** URL do endpoint SSE. Default: '/api/sse' */
  url?: string
  /** Chamado quando a conexão é estabelecida */
  onConnected?: () => void
  /** Chamado quando a conexão é perdida e será reconectada */
  onReconnecting?: () => void
}

/**
 * useSSE — hook para conexão persistente com Server-Sent Events.
 *
 * - Auto-reconexão com backoff exponencial (1s → 2s → 4s → 8s → 30s máx)
 * - Limpa a conexão ao desmontar o componente
 * - Handlers registrados via addEventListener/removeEventListener
 * - Zero polling — apenas eventos push do servidor
 *
 * Uso:
 *   const { addEventListener, removeEventListener } = useSSE()
 *
 *   useEffect(() => {
 *     addEventListener('dm.new', (data) => console.log(data))
 *     return () => removeEventListener('dm.new', handler)
 *   }, [])
 */
export function useSSE(options: UseSSEOptions = {}) {
  const { url = '/api/sse', onConnected, onReconnecting } = options

  const esRef        = useRef<EventSource | null>(null)
  const handlersRef  = useRef<Map<string, Set<SSEHandler>>>(new Map())
  const retryDelay   = useRef(1000)
  const retryTimer   = useRef<ReturnType<typeof setTimeout> | null>(null)
  const mounted      = useRef(true)

  const dispatch = useCallback((event: string, data: unknown) => {
    handlersRef.current.get(event)?.forEach(fn => {
      try { fn(data) } catch {}
    })
  }, [])

  const connect = useCallback(() => {
    if (!mounted.current) return
    if (esRef.current) {
      esRef.current.close()
      esRef.current = null
    }

    const es = new EventSource(url, { withCredentials: true })
    esRef.current = es

    es.addEventListener('connected', () => {
      retryDelay.current = 1000 // reset backoff
      onConnected?.()
    })

    // Listener genérico para qualquer evento nomeado
    const rawListener = (e: MessageEvent) => {
      try {
        const data = JSON.parse(e.data)
        dispatch(e.type, data)
      } catch {
        dispatch(e.type, e.data)
      }
    }

    const EVENTS = [
      'dm.new',
      'presence.change',
      'human_api.new',
      'human_api.timeout',
      'human_api.answered',
      'handoff.expired',
      'sla.breach',
      'thread.new',
    ]
    EVENTS.forEach(evt => es.addEventListener(evt, rawListener as EventListener))

    es.onerror = () => {
      es.close()
      esRef.current = null
      if (!mounted.current) return

      onReconnecting?.()
      const delay = Math.min(retryDelay.current, 30_000)
      retryDelay.current = Math.min(retryDelay.current * 2, 30_000)

      retryTimer.current = setTimeout(connect, delay)
    }
  }, [url, onConnected, onReconnecting, dispatch])

  useEffect(() => {
    mounted.current = true
    connect()
    return () => {
      mounted.current = false
      if (retryTimer.current) clearTimeout(retryTimer.current)
      esRef.current?.close()
      esRef.current = null
    }
  }, [connect])

  const addEventListener = useCallback((event: string, handler: SSEHandler) => {
    if (!handlersRef.current.has(event)) {
      handlersRef.current.set(event, new Set())
    }
    handlersRef.current.get(event)!.add(handler)
  }, [])

  const removeEventListener = useCallback((event: string, handler: SSEHandler) => {
    handlersRef.current.get(event)?.delete(handler)
  }, [])

  return { addEventListener, removeEventListener }
}
