/**
 * `useBans` hook.
 *
 * Fetches and manages paginated ban-list data from the dashboard endpoint.
 * Re-fetches automatically when `timeRange` or `page` changes.
 */

import { useCallback, useEffect, useRef, useState } from "react";
import { fetchBans } from "../api/dashboard";
import { handleFetchError } from "../utils/fetchError";
import type { DashboardBanItem, TimeRange, BanOriginFilter } from "../types/ban";

/** Items per page for the ban table. */
const PAGE_SIZE = 100;

/** Return value shape for {@link useBans}. */
export interface UseBansResult {
  /** Ban items for the current page. */
  banItems: DashboardBanItem[];
  /** Total records in the selected time window (for pagination). */
  total: number;
  /** Current 1-based page number. */
  page: number;
  /** Navigate to a specific page. */
  setPage: (p: number) => void;
  /** Whether a fetch is currently in flight. */
  loading: boolean;
  /** Error message if the last fetch failed, otherwise `null`. */
  error: string | null;
  /** Imperatively re-fetch the current page. */
  refresh: () => void;
}

/**
 * Fetch and manage dashboard ban-list data.
 *
 * Automatically re-fetches when `timeRange`, `origin`, or `page` changes.
 *
 * @param timeRange - Time-range preset that controls how far back to look.
 * @param origin    - Origin filter (default `"all"`).
 * @returns Current data, pagination state, loading flag, and a `refresh`
 *   callback.
 */
export function useBans(
  timeRange: TimeRange,
  origin: BanOriginFilter = "all",
  source: "fail2ban" | "archive" = "fail2ban",
): UseBansResult {
  const [banItems, setBanItems] = useState<DashboardBanItem[]>([]);
  const [total, setTotal] = useState<number>(0);
  const [page, setPage] = useState<number>(1);
  const [loading, setLoading] = useState<boolean>(true);
  const [error, setError] = useState<string | null>(null);
  const abortRef = useRef<AbortController | null>(null);

  // Reset page when time range, origin filter, or source changes.
  useEffect(() => {
    setPage(1);
  }, [timeRange, origin, source]);

  const doFetch = useCallback(async (): Promise<void> => {
    abortRef.current?.abort();
    const controller = new AbortController();
    abortRef.current = controller;

    setLoading(true);
    setError(null);

    try {
      const data = await fetchBans(timeRange, page, PAGE_SIZE, origin, source, controller.signal);
      if (controller.signal.aborted) return;
      setBanItems(data.items);
      setTotal(data.total);
    } catch (err: unknown) {
      if (controller.signal.aborted) return;
      handleFetchError(err, setError, "Failed to fetch bans");
    } finally {
      if (!controller.signal.aborted) {
        setLoading(false);
      }
    }
  }, [timeRange, page, origin, source]);

  // Stable ref to the latest doFetch so the refresh callback is always current.
  const doFetchRef = useRef(doFetch);
  doFetchRef.current = doFetch;

  useEffect(() => {
    void doFetch();
    return (): void => {
      abortRef.current?.abort();
    };
  }, [doFetch]);

  const refresh = useCallback((): void => {
    void doFetchRef.current();
  }, []);

  return {
    banItems,
    total,
    page,
    setPage,
    loading,
    error,
    refresh,
  };
}
