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

export interface UseDebouncedSearchOptions<T> {
  /** Funcion de fetch que recibe la query y retorna resultados */
  fetch_fn: (query: string) => Promise<T>
  /** Delay en ms antes de ejecutar la busqueda tras el ultimo cambio (default 300) */
  delay?: number
  /** Query inicial (default "") */
  initial_query?: string
  /** Si false no hace fetch cuando query es empty string (default true = fetch con "") */
  fetch_on_empty?: boolean
}

export interface UseDebouncedSearchResult<T> {
  /** Valor actual del input de busqueda */
  query: string
  /** Actualizar la query (dispara el debounce) */
  set_query: (q: string) => void
  /** Resultados de la ultima busqueda exitosa */
  results: T | null
  /** Si hay una busqueda en curso */
  loading: boolean
  /** Error si la ultima busqueda fallo */
  error: Error | null
  /** Limpiar query y resultados */
  clear: () => void
}

/**
 * Hook que combina un input con debounce y fetch automatico.
 * Espera N ms de inactividad tras el ultimo cambio de query antes de hacer la peticion.
 */
export function useDebouncedSearch<T>(
  opts: UseDebouncedSearchOptions<T>,
): UseDebouncedSearchResult<T> {
  const { fetch_fn, delay = 300, initial_query = '', fetch_on_empty = true } = opts

  const [query, setQuery] = useState(initial_query)
  const [results, setResults] = useState<T | null>(null)
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState<Error | null>(null)

  const mountedRef = useRef(true)
  const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)

  useEffect(() => {
    mountedRef.current = true
    return () => {
      mountedRef.current = false
    }
  }, [])

  useEffect(() => {
    if (!fetch_on_empty && query === '') {
      setResults(null)
      setLoading(false)
      return
    }

    setLoading(true)
    setError(null)

    if (timerRef.current !== null) {
      clearTimeout(timerRef.current)
    }

    timerRef.current = setTimeout(async () => {
      try {
        const data = await fetch_fn(query)
        if (!mountedRef.current) return
        setResults(data)
      } catch (err) {
        if (!mountedRef.current) return
        setError(err instanceof Error ? err : new Error(String(err)))
      } finally {
        if (mountedRef.current) setLoading(false)
      }
    }, delay)

    return () => {
      if (timerRef.current !== null) {
        clearTimeout(timerRef.current)
      }
    }
  }, [query, delay, fetch_on_empty]) // eslint-disable-line react-hooks/exhaustive-deps

  const setQueryDebounced = useCallback((q: string) => {
    setQuery(q)
  }, [])

  const clear = useCallback(() => {
    if (timerRef.current !== null) {
      clearTimeout(timerRef.current)
    }
    setQuery('')
    setResults(null)
    setLoading(false)
    setError(null)
  }, [])

  return { query, set_query: setQueryDebounced, results, loading, error, clear }
}
