/**
 * Base hook for fetching ban-by-country aggregates.
 *
 * This hook encapsulates the core logic for fetching country-level ban statistics,
 * maintaining abort-controller semantics, and exposing both the full response and
 * its individual fields for convenient destructuring.
 *
 * For a debounced version, use {@link useMapData}.
 * For a dashboard-specific wrapper, use {@link useDashboardCountryData}.
 */

import { useCallback, useEffect, useRef, useState } from "react";
import { fetchBansByCountry } from "../api/map";
import { handleFetchError, createStringErrorAdapter } from "../utils/fetchError";
import type { BanOriginFilter, TimeRange } from "../types/ban";
import type { BansByCountryResponse, MapBanItem } from "../types/map";

/**
 * Return value shape for {@link useBansByCountry}.
 */
export interface UseBansByCountryResult {
  /** Per-country ban counts (ISO alpha-2 → count). */
  countries: Record<string, number>;
  /** ISO alpha-2 → country name mapping. */
  countryNames: Record<string, string>;
  /** All ban records in the selected window. */
  bans: MapBanItem[];
  /** Total ban count. */
  total: number;
  /** True while a fetch is in flight. */
  loading: boolean;
  /** Error message or null. */
  error: string | null;
  /** Trigger a manual re-fetch. */
  refresh: () => void;
  /** The full response object (for callers that need raw data). */
  data: BansByCountryResponse | null;
}

/**
 * Fetch and manage ban-by-country data.
 *
 * This is the base hook shared by both `useDashboardCountryData` and `useMapData`.
 * It handles the fetching, abort-controller pattern, and state management.
 * Callers can add debouncing or other behaviour on top.
 *
 * @param timeRange - Time-range preset: `"24h"`, `"7d"`, `"30d"`, or `"365d"`.
 * @param origin - Origin filter: `"all"`, `"blocklist"`, or `"selfblock"`.
 * @param source - Data source: `"fail2ban"` or `"archive"`.
 * @param countryCode - Optional ISO alpha-2 country code to filter by.
 * @param autoFetch - If false, caller must invoke refresh() manually (default: true).
 * @returns Country data, loading state, error, and refresh function.
 */
export function useBansByCountry(
  timeRange: TimeRange,
  origin: BanOriginFilter,
  source: "fail2ban" | "archive" = "fail2ban",
  countryCode?: string,
  autoFetch: boolean = true,
): UseBansByCountryResult {
  const [data, setData] = useState<BansByCountryResponse | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const abortRef = useRef<AbortController | null>(null);

  const refresh = useCallback((): void => {
    // Abort any in-flight request from a previous filter selection.
    abortRef.current?.abort();
    const controller = new AbortController();
    abortRef.current = controller;

    setLoading(true);
    setError(null);

    fetchBansByCountry(timeRange, origin, source, countryCode, controller.signal)
      .then((resp) => {
        if (!controller.signal.aborted) {
          setData(resp);
        }
      })
      .catch((err: unknown) => {
        if (!controller.signal.aborted) {
          handleFetchError(err, createStringErrorAdapter(setError), "Failed to fetch ban-by-country data");
        }
      })
      .finally((): void => {
        if (!controller.signal.aborted) {
          setLoading(false);
        }
      });
  }, [timeRange, origin, source, countryCode]);

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

  return {
    countries: data?.countries ?? {},
    countryNames: data?.country_names ?? {},
    bans: data?.bans ?? [],
    total: data?.total ?? 0,
    loading,
    error,
    refresh,
    data,
  };
}
