import { useCallback } from 'react';
import { toast } from 'sonner';
import type { TRPCClientError } from '@trpc/client';

/**
 * Options for configuring optimistic mutations
 */
export interface OptimisticMutationOptions<TData, TVariables, TContext = unknown> {
  /** Function to apply optimistic update */
  onOptimisticUpdate?: (variables: TVariables) => TContext;
  /** Function to revert optimistic update on error */
  onRevert?: (context: TContext | undefined) => void;
  /** Function to clean up after mutation settles */
  onCleanup?: () => void;
  /** Success toast message */
  successMessage?: string;
  /** Error toast message (will be appended with actual error) */
  errorMessage?: string;
  /** Whether to show toast notifications */
  showToasts?: boolean;
}

/**
 * Higher-order function that wraps a tRPC mutation with optimistic updates
 */
export function useOptimisticMutation<TData, TVariables, TContext = unknown>(
  mutation: any, // tRPC mutation
  options: OptimisticMutationOptions<TData, TVariables, TContext> = {}
) {
  const {
    onOptimisticUpdate,
    onRevert,
    onCleanup,
    successMessage,
    errorMessage,
    showToasts = true,
  } = options;

  const mutate = useCallback(
    (variables: TVariables) => {
      let context: TContext | undefined;

      return mutation.mutate(variables, {
        onMutate: async (vars: TVariables) => {
          if (onOptimisticUpdate) {
            context = onOptimisticUpdate(vars);
          }
          return { context };
        },
        onSuccess: () => {
          if (successMessage && showToasts) {
            toast.success(successMessage);
          }
        },
        onError: (error: TRPCClientError<any>) => {
          if (onRevert && context !== undefined) {
            onRevert(context);
          }

          if (showToasts) {
            const message = errorMessage
              ? `${errorMessage}: ${error.message}`
              : `Error: ${error.message}`;
            toast.error(message);
          }
        },
        onSettled: () => {
          if (onCleanup) {
            onCleanup();
          }
        },
      });
    },
    [mutation, onOptimisticUpdate, onRevert, onCleanup, successMessage, errorMessage, showToasts]
  );

  const mutateAsync = useCallback(
    async (variables: TVariables): Promise<TData> => {
      let context: TContext | undefined;

      try {
        if (onOptimisticUpdate) {
          context = onOptimisticUpdate(variables);
        }

        const result = await mutation.mutateAsync(variables);

        if (successMessage && showToasts) {
          toast.success(successMessage);
        }

        return result;
      } catch (error) {
        if (onRevert && context !== undefined) {
          onRevert(context);
        }

        if (showToasts) {
          const message = errorMessage && error instanceof Error
            ? `${errorMessage}: ${error.message}`
            : `Error: ${error instanceof Error ? error.message : 'Unknown error'}`;
          toast.error(message);
        }

        throw error;
      } finally {
        if (onCleanup) {
          onCleanup();
        }
      }
    },
    [mutation, onOptimisticUpdate, onRevert, onCleanup, successMessage, errorMessage, showToasts]
  );

  return {
    mutate,
    mutateAsync,
    isPending: mutation.isPending,
    error: mutation.error,
    isError: mutation.isError,
    isSuccess: mutation.isSuccess,
    reset: mutation.reset,
  };
}