import { useRef, useCallback, useEffect, useState } from "react";
import { trpc } from "@/lib/trpc";
import { toast } from "sonner";

interface UseAutoSaveOptions {
  debounceMs?: number;
  onSaved?: () => void;
  onError?: (error: Error) => void;
}

export function useAutoSave({ debounceMs = 1000, onSaved, onError }: UseAutoSaveOptions = {}) {
  const [lastError, setLastError] = useState<Error | null>(null);
  const [retryCount, setRetryCount] = useState(0);
  const utils = trpc.useUtils();

  const updateMutation = trpc.updatePage.useMutation({
    onMutate: async (variables) => {
      // Cancel any outgoing refetches
      await utils.getPage.cancel({ id: variables.id });

      // Snapshot the previous value
      const previousPage = utils.getPage.getData({ id: variables.id });

      // Optimistically update the page in cache
      if (previousPage) {
        const optimisticUpdate = {
          ...previousPage,
          ...(variables.title !== undefined && { title: variables.title }),
          ...(variables.content !== undefined && { content: variables.content }),
          updatedAt: new Date().toISOString(),
        };
        utils.getPage.setData({ id: variables.id }, optimisticUpdate);
      }

      return { previousPage };
    },
    onSuccess: () => {
      setLastError(null);
      setRetryCount(0);
      onSaved?.();
    },
    onError: (error, variables, context) => {
      // Revert optimistic update
      if (context?.previousPage) {
        utils.getPage.setData({ id: variables.id }, context.previousPage);
      }
      setLastError(error);
      onError?.(error);
    },
    // Invalidation handled globally by MutationCache
  });
  const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const pendingRef = useRef<{
    id: string;
    title?: string;
    content?: string | null;
  } | null>(null);

  const flush = useCallback(async () => {
    if (timerRef.current) {
      clearTimeout(timerRef.current);
      timerRef.current = null;
    }
    if (pendingRef.current) {
      const data = pendingRef.current;
      pendingRef.current = null;

      try {
        await updateMutation.mutateAsync(data);
        return;
      } catch (error) {
        // Retry logic for transient failures
        if (retryCount < 2 && error instanceof Error) {
          setRetryCount(prev => prev + 1);
          pendingRef.current = data; // Restore data for retry

          // Exponential backoff: 1s, 2s
          const delay = 1000 * Math.pow(2, retryCount);
          setTimeout(() => void flush(), delay);
          return;
        }

        // Final failure - show user feedback
        toast.error(`Failed to save: ${error instanceof Error ? error.message : 'Unknown error'}`, {
          action: {
            label: 'Retry',
            onClick: () => {
              setRetryCount(0);
              pendingRef.current = data;
              void flush();
            },
          },
        });
        throw error;
      }
    }
    return Promise.resolve();
  }, [updateMutation, retryCount]);

  const save = useCallback(
    (id: string, data: { title?: string; content?: string | null }) => {
      pendingRef.current = { id, ...data };

      if (timerRef.current) {
        clearTimeout(timerRef.current);
      }

      timerRef.current = setTimeout(() => void flush(), debounceMs);
    },
    [debounceMs, flush],
  );

  // Flush on unmount
  useEffect(() => {
    return () => {
      if (timerRef.current) {
        clearTimeout(timerRef.current);
      }
      if (pendingRef.current) {
        // Fire off the save — mutation will complete asynchronously
        const data = pendingRef.current;
        pendingRef.current = null;
        updateMutation.mutate(data);
      }
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return {
    save,
    flush,
    isSaving: updateMutation.isPending,
    lastError,
    hasError: lastError !== null,
    retryCount,
  };
}
