import { useCallback, useEffect, useRef, useState } from 'react'
import { cacheGet, cacheHas, cacheIsStale, cacheSet } from './http_cache'
import type { FetchState } from '../../types/core/fetch_state'

export interface UseFetchOptions<T> {
  /** URL a fetchear, o funcion que retorna una Promise con los datos */
  url?: string
  /** Funcion de fetch alternativa (ej: api.get de api_client). Recibe la URL. */
  fetch_fn?: (url: string) => Promise<T>
  /** Si false, no hace fetch automatico al montar (default true) */
  enabled?: boolean
  /** Tiempo en ms antes de considerar datos stale (default 30000) */
  stale_time?: number
  /** Intervalo en ms para refetch automatico (0 = desactivado) */
  refetch_interval?: number
  /** Si refetchea al recuperar foco de la ventana (default true) */
  refetch_on_focus?: boolean
  /** Numero de reintentos ante error (default 0) */
  retry?: number
  /** Delay en ms entre reintentos (default 1000) */
  retry_delay?: number
}

/**
 * Hook GET con cache in-memory stale-while-revalidate.
 * Muestra datos cacheados inmediatamente mientras refetchea en background.
 */
export function useFetch<T>(opts: UseFetchOptions<T>): FetchState<T> {
  const {
    url = '',
    fetch_fn,
    enabled = true,
    stale_time = 30_000,
    refetch_interval = 0,
    refetch_on_focus = true,
    retry = 0,
    retry_delay = 1000,
  } = opts

  const cacheKey = url

  type FetchInternalState = Omit<FetchState<T>, 'refetch'>

  const [state, setState] = useState<FetchInternalState>(() => ({
    data: cacheHas(cacheKey) ? cacheGet<T>(cacheKey) : null,
    error: null,
    loading: enabled && (!cacheHas(cacheKey) || cacheIsStale(cacheKey, stale_time)),
    is_stale: cacheHas(cacheKey) && cacheIsStale(cacheKey, stale_time),
  }))

  const mountedRef = useRef(true)
  const retryCountRef = useRef(0)

  const doFetch = useCallback(async (): Promise<T> => {
    if (!mountedRef.current) throw new Error('unmounted')

    setState((s: FetchInternalState) => ({ ...s, loading: true, error: null }))

    try {
      const data = fetch_fn
        ? await fetch_fn(url)
        : await fetch(url, { headers: { Accept: 'application/json' } }).then((r) => {
            if (!r.ok) throw new Error(`HTTP ${r.status}`)
            return r.json() as Promise<T>
          })

      if (!mountedRef.current) throw new Error('unmounted')

      cacheSet(cacheKey, data)
      retryCountRef.current = 0

      setState({ data, loading: false, error: null, is_stale: false })
      return data
    } catch (err) {
      if (!mountedRef.current) throw err

      if (retryCountRef.current < retry) {
        retryCountRef.current += 1
        await new Promise((resolve) => setTimeout(resolve, retry_delay))
        return doFetch()
      }

      const error = err instanceof Error ? err : new Error(String(err))
      setState((s: FetchInternalState) => ({ ...s, loading: false, error }))
      throw error
    }
  }, [url, fetch_fn, cacheKey, stale_time, retry, retry_delay])

  // Fetch inicial
  useEffect(() => {
    mountedRef.current = true

    if (enabled && url && (!cacheHas(cacheKey) || cacheIsStale(cacheKey, stale_time))) {
      doFetch().catch(() => {})
    } else if (enabled && url && cacheHas(cacheKey)) {
      // Datos en cache no stale — actualizar estado si es necesario
      setState((s: FetchInternalState) => ({
        ...s,
        data: cacheGet<T>(cacheKey),
        loading: false,
        is_stale: false,
      }))
    }

    return () => {
      mountedRef.current = false
    }
  }, [enabled, url, stale_time]) // eslint-disable-line react-hooks/exhaustive-deps

  // Refetch por intervalo
  useEffect(() => {
    if (!refetch_interval || !enabled || !url) return

    const id = setInterval(() => {
      doFetch().catch(() => {})
    }, refetch_interval)

    return () => clearInterval(id)
  }, [refetch_interval, enabled, url, doFetch])

  // Refetch al recuperar foco
  useEffect(() => {
    if (!refetch_on_focus || !enabled || !url) return

    const handler = () => {
      if (cacheIsStale(cacheKey, stale_time)) {
        doFetch().catch(() => {})
      }
    }

    window.addEventListener('focus', handler)
    return () => window.removeEventListener('focus', handler)
  }, [refetch_on_focus, enabled, url, cacheKey, stale_time, doFetch])

  return { ...state, refetch: doFetch }
}
