"use client";

import { useState, useCallback, useRef } from "react";
import { toast } from "sonner";
import type { AxiosError } from "axios";

interface UseApiMutationOptions<TOutput> {
  onSuccess?: (data: TOutput) => void;
  onError?: (error: string) => void;
  successMessage?: string;
  /** When set, shows a sonner loading toast (spinner) while in flight, replaced in place by the success/error toast. */
  loadingMessage?: string;
}

interface UseApiMutationResult<TInput, TOutput> {
  mutate: (input: TInput) => Promise<TOutput | undefined>;
  loading: boolean;
  error: string | null;
}

export function useApiMutation<TInput, TOutput = void>(
  mutationFn: (input: TInput) => Promise<TOutput>,
  options?: UseApiMutationOptions<TOutput>
): UseApiMutationResult<TInput, TOutput> {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const optionsRef = useRef(options);
  optionsRef.current = options;
  const mutationFnRef = useRef(mutationFn);
  mutationFnRef.current = mutationFn;

  const mutate = useCallback(async (input: TInput): Promise<TOutput | undefined> => {
    setLoading(true);
    setError(null);
    const toastId = optionsRef.current?.loadingMessage
      ? toast.loading(optionsRef.current.loadingMessage)
      : undefined;
    try {
      const result = await mutationFnRef.current(input);
      if (optionsRef.current?.successMessage) {
        toast.success(optionsRef.current.successMessage, toastId !== undefined ? { id: toastId } : undefined);
      } else if (toastId !== undefined) {
        toast.dismiss(toastId);
      }
      optionsRef.current?.onSuccess?.(result);
      return result;
    } catch (err) {
      const axiosError = err as AxiosError<{ message?: string }>;
      const message =
        axiosError.response?.data?.message ||
        axiosError.message ||
        "Une erreur est survenue";
      setError(message);
      toast.error(message, toastId !== undefined ? { id: toastId } : undefined);
      optionsRef.current?.onError?.(message);
      return undefined;
    } finally {
      setLoading(false);
    }
  }, []);

  return { mutate, loading, error };
}
