/**
 * `useMapData` hook — fetches and manages ban-by-country data.
 */

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

// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------

/** Debounce delay in milliseconds before firing a fetch after filter changes. */
const DEBOUNCE_MS = 300;

// ---------------------------------------------------------------------------
// Return type
// ---------------------------------------------------------------------------

export interface UseMapDataResult {
  /** 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;
}

// ---------------------------------------------------------------------------
// Hook
// ---------------------------------------------------------------------------

export function useMapData(
  range: TimeRange = "24h",
  origin: BanOriginFilter = "all",
  source: "fail2ban" | "archive" = "fail2ban",
  countryCode?: string,
): UseMapDataResult {
  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 debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  const load = useCallback((): void => {
    // Cancel any pending debounce timer.
    if (debounceRef.current != null) {
      clearTimeout(debounceRef.current);
    }
    // Show loading immediately so the skeleton / spinner appears.
    setLoading(true);
    setError(null);

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

      fetchBansByCountry(range, origin, source, countryCode)
        .then((resp) => {
          setData(resp);
        })
        .catch((err: unknown) => {
          handleFetchError(err, setError, "Failed to fetch map data");
        })
        .finally((): void => {
          setLoading(false);
        });
    }, DEBOUNCE_MS);
  }, [range, origin, source, countryCode]);

  useEffect((): (() => void) => {
    load();
    return (): void => {
      if (debounceRef.current != null) {
        clearTimeout(debounceRef.current);
      }
      abortRef.current?.abort();
    };
  }, [load]);

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

