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

export interface UseInfiniteScrollOptions<T> {
  /** Funcion de fetch que recibe el cursor/pagina actual y retorna la pagina de datos */
  fetch_fn: (cursor: string | number | null) => Promise<InfiniteScrollPage<T>>
  /** Cursor inicial (null para empezar desde el principio) */
  initial_cursor?: string | number | null
  /** Si el primer fetch es automatico al montar (default true) */
  enabled?: boolean
}

/** Pagina retornada por fetch_fn */
export interface InfiniteScrollPage<T> {
  /** Datos de esta pagina */
  items: T[]
  /** Cursor para la siguiente pagina. null/undefined = no hay mas paginas. */
  next_cursor?: string | number | null
}

/** Estado del hook de paginacion infinita */
export interface UseInfiniteScrollResult<T> {
  /** Todos los items concatenados de todas las paginas */
  data: T[]
  /** Si hay una peticion en curso */
  loading: boolean
  /** Error si la ultima peticion fallo */
  error: Error | null
  /** Si hay mas paginas disponibles */
  has_more: boolean
  /** Cargar la siguiente pagina */
  load_more: () => void
  /** Resetear al estado inicial y refetchear desde el principio */
  reset: () => void
}

/**
 * Hook para paginacion infinita cursor-based y offset-based.
 * Concatena paginas en un solo array. Expone load_more, has_more y reset.
 */
export function useInfiniteScroll<T>(
  opts: UseInfiniteScrollOptions<T>,
): UseInfiniteScrollResult<T> {
  const { fetch_fn, initial_cursor = null, enabled = true } = opts

  const [data, setData] = useState<T[]>([])
  const [loading, setLoading] = useState(enabled)
  const [error, setError] = useState<Error | null>(null)
  const [hasMore, setHasMore] = useState(true)

  const cursorRef = useRef<string | number | null>(initial_cursor)
  const loadingRef = useRef(false)
  const mountedRef = useRef(true)

  const fetchPage = useCallback(
    async (cursor: string | number | null): Promise<void> => {
      if (loadingRef.current) return
      loadingRef.current = true

      setLoading(true)
      setError(null)

      try {
        const page = await fetch_fn(cursor)

        if (!mountedRef.current) return

        setData((prev: T[]) => (cursor === initial_cursor ? page.items : [...prev, ...page.items]))

        const nextCursor = page.next_cursor ?? null
        cursorRef.current = nextCursor
        setHasMore(nextCursor !== null && nextCursor !== undefined)
      } catch (err) {
        if (!mountedRef.current) return
        setError(err instanceof Error ? err : new Error(String(err)))
      } finally {
        if (mountedRef.current) setLoading(false)
        loadingRef.current = false
      }
    },
    [fetch_fn, initial_cursor],
  )

  // Fetch inicial
  const initializedRef = useRef(false)
  if (!initializedRef.current && enabled) {
    initializedRef.current = true
    // eslint-disable-next-line @typescript-eslint/no-floating-promises
    fetchPage(initial_cursor)
  }

  const loadMore = useCallback(() => {
    if (!hasMore || loadingRef.current) return
    fetchPage(cursorRef.current).catch(() => {})
  }, [fetchPage, hasMore])

  const reset = useCallback(() => {
    cursorRef.current = initial_cursor
    loadingRef.current = false
    setData([])
    setError(null)
    setHasMore(true)
    fetchPage(initial_cursor).catch(() => {})
  }, [fetchPage, initial_cursor])

  return { data, loading, error, has_more: hasMore, load_more: loadMore, reset }
}
