import { useState, useCallback, useRef, useEffect } from "react";
import { apiClient, type ApiRequestOptions } from "@/lib/api/client";

interface UseApiMutationOptions<TData, TVariables> {
  onSuccess?: (data: TData, variables: TVariables) => void;
  onError?: (error: unknown, variables: TVariables) => void;
  optimisticUpdate?: (variables: TVariables) => TData | null;
  onOptimisticError?: (error: unknown, variables: TVariables) => void;
  invalidateQueries?: string[]; // URLs to invalidate after mutation
}

interface UseApiMutationReturn<TData, TVariables> {
  mutate: (variables: TVariables, options?: ApiRequestOptions) => Promise<TData | undefined>;
  mutateAsync: (variables: TVariables, options?: ApiRequestOptions) => Promise<TData>;
  loading: boolean;
  error: unknown | null;
  reset: () => void;
}

export function useApiMutation<TData = unknown, TVariables = unknown>(
  url: string,
  method: "POST" | "PUT" | "DELETE" | "PATCH" = "POST",
  options: UseApiMutationOptions<TData, TVariables> = {},
): UseApiMutationReturn<TData, TVariables> {
  const { onSuccess, onError, optimisticUpdate, onOptimisticError, invalidateQueries } = options;

  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<unknown | null>(null);

  const isMountedRef = useRef(true);
  const optimisticDataRef = useRef<TData | null>(null);

  useEffect(() => {
    isMountedRef.current = true;
    return () => {
      isMountedRef.current = false;
    };
  }, []);

  const mutateAsync = useCallback(
    async (variables: TVariables, requestOptions?: ApiRequestOptions): Promise<TData> => {
      setLoading(true);
      setError(null);

      // Apply optimistic update if provided
      if (optimisticUpdate) {
        try {
          optimisticDataRef.current = optimisticUpdate(variables);
        } catch (err) {
          onOptimisticError?.(err, variables);
        }
      }

      try {
        let response;
        const body = variables instanceof FormData ? variables : variables;

        switch (method) {
          case "POST":
            response = await apiClient.post<TData>(url, body, requestOptions);
            break;
          case "PUT":
            response = await apiClient.put<TData>(url, body, requestOptions);
            break;
          case "PATCH":
            response = await apiClient.request<TData>(url, {
              ...requestOptions,
              method: "PATCH",
              body: variables instanceof FormData ? variables : JSON.stringify(variables),
              headers: variables instanceof FormData
                ? requestOptions?.headers
                : {
                    ...requestOptions?.headers,
                    "Content-Type": "application/json",
                  },
            });
            break;
          case "DELETE":
            response = await apiClient.delete<TData>(url, requestOptions);
            break;
          default:
            throw new Error(`Unsupported method: ${method}`);
        }

        if (!isMountedRef.current) return response.data;

        // Invalidate cache for specified queries
        if (invalidateQueries) {
          invalidateQueries.forEach((queryUrl) => {
            apiClient.clearCacheForUrl(queryUrl);
          });
        }

        optimisticDataRef.current = null;
        onSuccess?.(response.data, variables);
        return response.data;
      } catch (err) {
        if (!isMountedRef.current) throw err;

        optimisticDataRef.current = null;
        setError(err);
        onError?.(err, variables);
        throw err;
      } finally {
        if (isMountedRef.current) {
          setLoading(false);
        }
      }
    },
    [url, method, onSuccess, onError, optimisticUpdate, onOptimisticError, invalidateQueries],
  );

  const mutate = useCallback(
    async (
      variables: TVariables,
      requestOptions?: ApiRequestOptions,
    ): Promise<TData | undefined> => {
      try {
        return await mutateAsync(variables, requestOptions);
      } catch {
        // Error is handled in mutateAsync
        return undefined;
      }
    },
    [mutateAsync],
  );

  const reset = useCallback(() => {
    setError(null);
    setLoading(false);
    optimisticDataRef.current = null;
  }, []);

  return {
    mutate,
    mutateAsync,
    loading,
    error,
    reset,
  };
}

