/**
 * Generic hook for loading list data from an API endpoint.
 */
import { useCallback, useEffect, useRef, useState } from "react";
import { handleFetchError } from "../utils/fetchError";

export interface UseListDataOptions<TResponse, TItem> {
  fetcher: (signal: AbortSignal) => Promise<TResponse>;
  selector: (response: TResponse) => TItem[];
  errorMessage: string;
  onSuccess?: (response: TResponse) => void;
  initialItems?: TItem[];
}

export interface UseListDataResult<TItem> {
  items: TItem[];
  loading: boolean;
  error: string | null;
  refresh: () => void;
}

/**
 * Load a list response and expose refresh semantics with abort support.
 */
export function useListData<TResponse, TItem>(
  options: UseListDataOptions<TResponse, TItem>,
): UseListDataResult<TItem> {
  const { fetcher, selector, errorMessage, onSuccess, initialItems } = options;
  const [items, setItems] = useState<TItem[]>(initialItems ?? []);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const abortRef = useRef<AbortController | null>(null);

  const refresh = useCallback((): void => {
    abortRef.current?.abort();
    const controller = new AbortController();
    abortRef.current = controller;

    setLoading(true);
    setError(null);

    fetcher(controller.signal)
      .then((response) => {
        if (controller.signal.aborted) return;
        setItems(selector(response));
        if (onSuccess) {
          onSuccess(response);
        }
      })
      .catch((err: unknown) => {
        if (controller.signal.aborted) return;
        handleFetchError(err, setError, errorMessage);
      })
      .finally(() => {
        if (!controller.signal.aborted) {
          setLoading(false);
        }
      });
  }, [fetcher, selector, errorMessage, onSuccess]);

  useEffect(() => {
    refresh();

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

  return { items, loading, error, refresh };
}
