/**
 * React hook for paginated jailed IPs within a specific jail.
 */

import { useCallback, useEffect, useRef, useState } from "react";
import { fetchJailBannedIps, unbanIp } from "../api/jails";
import { handleFetchError } from "../utils/fetchError";
import type { ActiveBan } from "../types/jail";

export interface UseJailBannedIpsResult {
  items: ActiveBan[];
  total: number;
  page: number;
  pageSize: number;
  search: string;
  loading: boolean;
  error: string | null;
  opError: string | null;
  refresh: () => Promise<void>;
  setPage: (page: number) => void;
  setPageSize: (size: number) => void;
  setSearch: (term: string) => void;
  unban: (ip: string) => Promise<void>;
}

export function useJailBannedIps(jailName: string): UseJailBannedIpsResult {
  const [items, setItems] = useState<ActiveBan[]>([]);
  const [total, setTotal] = useState(0);
  const [page, setPage] = useState(1);
  const [pageSize, setPageSize] = useState(25);
  const [search, setSearch] = useState("");
  const [debouncedSearch, setDebouncedSearch] = useState("");
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [opError, setOpError] = useState<string | null>(null);
  const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const abortRef = useRef<AbortController | null>(null);

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

    if (!jailName) {
      setItems([]);
      setTotal(0);
      setLoading(false);
      return;
    }

    setLoading(true);
    setError(null);

    try {
      const resp = await fetchJailBannedIps(
        jailName,
        page,
        pageSize,
        debouncedSearch || undefined,
        signal,
      );
      if (signal.aborted) {
        return;
      }
      setItems(resp.items);
      setTotal(resp.total);
    } catch (err: unknown) {
      if (signal.aborted) {
        return;
      }
      handleFetchError(err, setError, "Failed to fetch jailed IPs");
    } finally {
      if (!signal.aborted) {
        setLoading(false);
      }
    }
  }, [jailName, page, pageSize, debouncedSearch]);

  useEffect(() => {
    if (debounceRef.current !== null) {
      clearTimeout(debounceRef.current);
    }

    debounceRef.current = setTimeout(() => {
      setDebouncedSearch(search);
      setPage(1);
    }, 300);

    return (): void => {
      if (debounceRef.current !== null) {
        clearTimeout(debounceRef.current);
      }
    };
  }, [search]);

  useEffect(() => {
    void load();

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

  const unban = useCallback(async (ip: string): Promise<void> => {
    setOpError(null);
    try {
      await unbanIp(ip, jailName);
      await load();
    } catch (err: unknown) {
      setOpError(err instanceof Error ? err.message : String(err));
    }
  }, [jailName, load]);

  return {
    items,
    total,
    page,
    pageSize,
    search,
    loading,
    error,
    opError,
    refresh: load,
    setPage,
    setPageSize,
    setSearch,
    unban,
  };
}
