import { useCallback, useEffect, useRef, useState } from 'react';
import { useDebouncedCallback } from 'use-debounce';

interface UseAutoSaveOptions<T> {
  /** Data to auto-save */
  data: T;
  /** Function to save the data */
  onSave: (data: T) => Promise<void>;
  /** Debounce delay in milliseconds (default: 2000) */
  debounceMs?: number;
  /** Whether auto-save is enabled (default: true) */
  enabled?: boolean;
  /** Callback when save starts */
  onSaveStart?: () => void;
  /** Callback when save succeeds */
  onSaveSuccess?: () => void;
  /** Callback when save fails */
  onSaveError?: (error: Error) => void;
}

interface UseAutoSaveReturn {
  /** Whether there are unsaved changes */
  isDirty: boolean;
  /** Whether a save is in progress */
  isSaving: boolean;
  /** Last error from save attempt */
  error: Error | null;
  /** Manually trigger a save */
  saveNow: () => Promise<void>;
  /** Mark as not dirty (e.g., after manual save) */
  markClean: () => void;
}

/**
 * Hook for auto-saving data with debounce.
 * Tracks dirty state and provides manual save capability.
 */
export function useAutoSave<T>({
  data,
  onSave,
  debounceMs = 2000,
  enabled = true,
  onSaveStart,
  onSaveSuccess,
  onSaveError,
}: UseAutoSaveOptions<T>): UseAutoSaveReturn {
  const [isDirty, setIsDirty] = useState(false);
  const [isSaving, setIsSaving] = useState(false);
  const [error, setError] = useState<Error | null>(null);

  // Keep a ref to the latest data for saving
  const dataRef = useRef(data);
  const initialDataRef = useRef<string | null>(null);

  // Store initial data on first render
  useEffect(() => {
    if (initialDataRef.current === null) {
      initialDataRef.current = JSON.stringify(data);
    }
  }, []);

  // Update the data ref when data changes
  useEffect(() => {
    dataRef.current = data;
  }, [data]);

  // Perform the actual save
  const performSave = useCallback(async () => {
    if (!enabled || isSaving) return;

    setIsSaving(true);
    setError(null);
    onSaveStart?.();

    try {
      await onSave(dataRef.current);
      setIsDirty(false);
      onSaveSuccess?.();
    } catch (err) {
      const error = err instanceof Error ? err : new Error(String(err));
      setError(error);
      onSaveError?.(error);
    } finally {
      setIsSaving(false);
    }
  }, [enabled, isSaving, onSave, onSaveStart, onSaveSuccess, onSaveError]);

  // Debounced save function
  const debouncedSave = useDebouncedCallback(performSave, debounceMs);

  // Track changes and trigger debounced save
  useEffect(() => {
    if (!enabled || initialDataRef.current === null) return;

    const currentData = JSON.stringify(data);
    const hasChanged = currentData !== initialDataRef.current;

    if (hasChanged && !isDirty) {
      setIsDirty(true);
    }

    if (hasChanged) {
      debouncedSave();
    }
  }, [data, enabled, isDirty, debouncedSave]);

  // Manual save function
  const saveNow = useCallback(async () => {
    debouncedSave.cancel();
    await performSave();
  }, [debouncedSave, performSave]);

  // Mark as clean (e.g., after successful post)
  const markClean = useCallback(() => {
    setIsDirty(false);
    initialDataRef.current = JSON.stringify(dataRef.current);
  }, []);

  return {
    isDirty,
    isSaving,
    error,
    saveNow,
    markClean,
  };
}
