import { useState, useCallback, useRef, useEffect } from 'react';

export interface UseApiState<T> {
  data: T | null;
  loading: boolean;
  error: string | null;
}

export interface UseApiReturn<T, Args extends any[]> extends UseApiState<T> {
  execute: (...args: Args) => Promise<T | null>;
  reset: () => void;
}

/**
 * Custom hook for managing async API calls with loading and error states
 *
 * @param apiCall - The async function to execute
 * @param options - Configuration options
 * @returns Object containing data, loading, error, execute function, and reset function
 *
 * @example
 * const { data, loading, error, execute } = useApi(
 *   (id: number) => api.getUser(id),
 *   { executeOnMount: false }
 * );
 *
 * // Later...
 * await execute(123);
 */
export function useApi<T, Args extends any[] = []>(
  apiCall: (...args: Args) => Promise<T>,
  options: {
    executeOnMount?: boolean;
    onSuccess?: (data: T) => void;
    onError?: (error: string) => void;
  } = {}
): UseApiReturn<T, Args> {
  const { executeOnMount = false, onSuccess, onError } = options;

  const [state, setState] = useState<UseApiState<T>>({
    data: null,
    loading: false,
    error: null,
  });

  const abortControllerRef = useRef<AbortController | null>(null);
  const mountedRef = useRef(true);

  const execute = useCallback(
    async (...args: Args): Promise<T | null> => {
      // Cancel any pending request
      if (abortControllerRef.current) {
        abortControllerRef.current.abort();
      }

      abortControllerRef.current = new AbortController();

      setState((prev) => ({ ...prev, loading: true, error: null }));

      try {
        const result = await apiCall(...args);

        if (!mountedRef.current) return null;

        setState({ data: result, loading: false, error: null });

        if (onSuccess) {
          onSuccess(result);
        }

        return result;
      } catch (err) {
        if (!mountedRef.current) return null;

        // Don't treat abort as an error
        if (err instanceof Error && err.name === 'AbortError') {
          return null;
        }

        const errorMessage = err instanceof Error ? err.message : 'An unknown error occurred';

        setState({ data: null, loading: false, error: errorMessage });

        if (onError) {
          onError(errorMessage);
        }

        return null;
      }
    },
    [apiCall, onSuccess, onError]
  );

  const reset = useCallback(() => {
    setState({ data: null, loading: false, error: null });
  }, []);

  useEffect(() => {
    mountedRef.current = true;

    if (executeOnMount) {
      execute(...([] as unknown as Args));
    }

    return () => {
      mountedRef.current = false;
      if (abortControllerRef.current) {
        abortControllerRef.current.abort();
      }
    };
  }, [execute, executeOnMount]);

  return {
    ...state,
    execute,
    reset,
  };
}
