'use client'
import { useState, useEffect, useCallback, useRef } from 'react'
import { cmsApi } from './api.js'

export interface UseApiDataOptions {
  maxRetries?: number
  /** Base delay in ms for exponential backoff (default 1000). Actual delay = base * 2^attempt. */
  retryBaseMs?: number
  /**
   * When set, successful responses are cached in-memory keyed by endpoint
   * for this many ms and reused on the next mount, skipping the network
   * (cache-first). Opt-in — omit to keep the default always-fetch
   * behaviour. `refetch()` always bypasses the cache. Useful for views
   * that remount frequently (e.g. dashboard navigation) so they don't
   * re-issue the same requests on every visit.
   */
  cacheMs?: number
}

/**
 * Process-wide response cache for `cacheMs` consumers. Keyed by endpoint
 * (note: the locale query param is appended downstream by `cmsApi`, so a
 * locale switch is not reflected here — only enable `cacheMs` for
 * locale-invariant endpoints like `/stats`).
 */
const responseCache = new Map<string, { data: unknown; at: number }>()

function readCache<T>(endpoint: string, cacheMs: number): { value: T | null } | null {
  const hit = responseCache.get(endpoint)
  if (hit && Date.now() - hit.at < cacheMs) return { value: hit.data as T | null }
  return null
}

/** Test-only: drop all cached responses so cases don't bleed into each other. */
export function __clearApiDataCache(): void {
  responseCache.clear()
}

export interface UseApiDataResult<T> {
  data: T | null
  loading: boolean
  error: string | null
  /** True when all retries are exhausted and the request permanently failed. */
  exhausted: boolean
  refetch: () => void
}

export function useApiData<T>(
  endpoint: string | null,
  defaultValue: T | null = null,
  options: UseApiDataOptions = {},
): UseApiDataResult<T> {
  const { maxRetries = 3, retryBaseMs = 1000, cacheMs } = options
  // Read the cache synchronously for the initial state so a warm cache
  // hit paints immediately instead of flashing a loading spinner.
  const cachedInitial = endpoint && cacheMs ? readCache<T>(endpoint, cacheMs) : null
  const [data, setData] = useState<T | null>(cachedInitial ? cachedInitial.value : defaultValue)
  const [loading, setLoading] = useState(endpoint !== null && !cachedInitial)
  const [error, setError] = useState<string | null>(null)
  const [exhausted, setExhausted] = useState(false)
  const abortRef = useRef<AbortController | null>(null)
  const retryCountRef = useRef(0)
  const retryTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)

  const fetchData = useCallback(
    async (isRetry = false, forceRefresh = false) => {
      if (!endpoint) {
        setLoading(false)
        return
      }

      // Cache-first: a fresh cached response short-circuits the network.
      // `refetch()` passes `forceRefresh` to bypass this.
      if (!isRetry && !forceRefresh && cacheMs) {
        const hit = readCache<T>(endpoint, cacheMs)
        if (hit) {
          setData(hit.value)
          setError(null)
          setLoading(false)
          return
        }
      }

      abortRef.current?.abort()
      const controller = new AbortController()
      abortRef.current = controller

      if (!isRetry) {
        retryCountRef.current = 0
        setExhausted(false)
      }

      setLoading(true)
      setError(null)

      const res = await cmsApi<T>(endpoint, { signal: controller.signal })

      if (controller.signal.aborted) return

      if (res.error) {
        if (retryCountRef.current < maxRetries) {
          const delay = retryBaseMs * Math.pow(2, retryCountRef.current)
          retryCountRef.current += 1
          retryTimerRef.current = setTimeout(() => fetchData(true), delay)
          return
        }
        setError(res.error)
        setExhausted(true)
        setLoading(false)
      } else {
        const value = res.data ?? null
        setData(value)
        setLoading(false)
        retryCountRef.current = 0
        if (cacheMs) responseCache.set(endpoint, { data: value, at: Date.now() })
      }
    },
    [endpoint, maxRetries, retryBaseMs, cacheMs],
  )

  const manualRefetch = useCallback(() => {
    if (retryTimerRef.current) {
      clearTimeout(retryTimerRef.current)
      retryTimerRef.current = null
    }
    retryCountRef.current = 0
    setExhausted(false)
    fetchData(false, true)
  }, [fetchData])

  useEffect(() => {
    fetchData(false)
    return () => {
      abortRef.current?.abort()
      if (retryTimerRef.current) {
        clearTimeout(retryTimerRef.current)
        retryTimerRef.current = null
      }
    }
  }, [fetchData])

  return { data, loading, error, exhausted, refetch: manualRefetch }
}
