import { useState, useCallback, useEffect, useRef } from 'react';
import { toast } from 'react-toastify';
import { logger } from '../utils/logger';

interface UseApiCallOptions {
  showSuccessToast?: boolean;
  successMessage?: string;
  showErrorToast?: boolean;
  onSuccess?: (data: any) => void;
  onError?: (error: any) => void;
  cancelOnUnmount?: boolean; // Cancel request on component unmount
}

interface UseApiCallReturn<T> {
  execute: (...args: any[]) => Promise<T | undefined>;
  isLoading: boolean;
  error: Error | null;
  clearError: () => void;
  cancel: () => void; // Manual cancellation function
}

/**
 * Custom hook for handling API calls with loading states and error handling
 * Includes request cancellation on component unmount to prevent memory leaks
 * 
 * @param apiFunction - The async API function to call
 * @param options - Configuration options
 * @returns Object with execute function, loading state, error state, clearError function, and cancel function
 */
export function useApiCall<T = any>(
  apiFunction: (...args: any[]) => Promise<T>,
  options: UseApiCallOptions = {}
): UseApiCallReturn<T> {
  const {
    showSuccessToast = false,
    successMessage = 'Operation completed successfully',
    showErrorToast = true,
    onSuccess,
    onError,
    cancelOnUnmount = true, // Default to true for safety
  } = options;

  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<Error | null>(null);
  const abortControllerRef = useRef<AbortController | null>(null);
  const mountedRef = useRef(true);

  // Track component mount status
  useEffect(() => {
    mountedRef.current = true;
    return () => {
      mountedRef.current = false;
      // Cancel pending request on unmount if enabled
      if (cancelOnUnmount && abortControllerRef.current) {
        abortControllerRef.current.abort();
        abortControllerRef.current = null;
      }
    };
  }, [cancelOnUnmount]);

  const cancel = useCallback(() => {
    if (abortControllerRef.current) {
      abortControllerRef.current.abort();
      abortControllerRef.current = null;
      setIsLoading(false);
    }
  }, []);

  const execute = useCallback(
    async (...args: any[]): Promise<T | undefined> => {
      // Cancel any previous request
      if (abortControllerRef.current) {
        abortControllerRef.current.abort();
      }

      // Create new AbortController for this request
      const abortController = new AbortController();
      abortControllerRef.current = abortController;

      try {
        setIsLoading(true);
        setError(null);

        // Check if component is still mounted before making request
        if (!mountedRef.current) {
          return undefined;
        }

        // Check if apiFunction supports AbortSignal
        // If the function accepts a signal parameter, pass it
        let result: T;
        if (args.length > 0 && typeof args[args.length - 1] === 'object' && 'signal' in args[args.length - 1]) {
          // Function already has signal in arguments
          result = await apiFunction(...args);
        } else {
          // Try to pass signal as last argument if function supports it
          try {
            result = await apiFunction(...args, { signal: abortController.signal });
          } catch (err: any) {
            // If function doesn't support signal, try without it
            if (err.name === 'TypeError' && err.message.includes('signal')) {
              result = await apiFunction(...args);
            } else {
              throw err;
            }
          }
        }

        // Check if component is still mounted before updating state
        if (!mountedRef.current) {
          return undefined;
        }

        // Check if request was cancelled
        if (abortController.signal.aborted) {
          return undefined;
        }

        if (showSuccessToast) {
          toast.success(successMessage);
        }

        if (onSuccess) {
          onSuccess(result);
        }

        abortControllerRef.current = null;
        return result;
      } catch (err: any) {
        // Don't handle errors if component is unmounted or request was cancelled
        if (!mountedRef.current || abortController.signal.aborted || err.name === 'AbortError') {
          return undefined;
        }

        const errorMessage =
          err?.response?.data?.message ||
          err?.response?.data?.detail ||
          err?.message ||
          'An error occurred. Please try again.';

        const apiError = err instanceof Error ? err : new Error(errorMessage);
        setError(apiError);

        logger.error('API call failed', err);

        if (showErrorToast && !err.isCancelled) {
          toast.error(errorMessage);
        }

        if (onError && !err.isCancelled) {
          onError(err);
        }

        throw err;
      } finally {
        // Only update loading state if component is still mounted
        if (mountedRef.current) {
          setIsLoading(false);
        }
        abortControllerRef.current = null;
      }
    },
    [apiFunction, showSuccessToast, successMessage, showErrorToast, onSuccess, onError]
  );

  const clearError = useCallback(() => {
    setError(null);
  }, []);

  return { execute, isLoading, error, clearError, cancel };
}

