import { useCallback, useEffect, useMemo, useRef, useState } from 'react';

/**
 * useUndoRedo Hook - Durum geçmişini yönetmek ve geri/ileri alma (undo/redo) 
 * işlevselliği sağlamak için kullanılır.
 */

interface UseUndoRedoOptions<T> {
  maxHistory?: number;
  storageKey?: string;
  debounceMs?: number;
}

interface UndoRedoState<T> {
  past: T[];
  present: T;
  future: T[];
}

export function useUndoRedo<T>(
  initialState: T,
  options: UseUndoRedoOptions<T> = {}
) {
  const { maxHistory = 50, storageKey, debounceMs = 0 } = options;

  // Başlangıç durumunu yükle (LocalStorage varsa oradan, yoksa initialState)
  const getInitialState = (): UndoRedoState<T> => {
    if (storageKey) {
      try {
        const saved = localStorage.getItem(storageKey);
        if (saved) {
          const parsed = JSON.parse(saved);
          return {
            past: [],
            present: parsed,
            future: [],
          };
        }
      } catch (error) {
        console.error('LocalStorage yükleme hatası:', error);
      }
    }
    return {
      past: [],
      present: initialState,
      future: [],
    };
  };

  const [state, setStateInternal] = useState<UndoRedoState<T>>(getInitialState);
  
  // Zaman tabanlı geçmiş (debounce) için ref'ler
  const timerRef = useRef<NodeJS.Timeout | null>(null);
  const pendingStateRef = useRef<T | null>(null);

  // LocalStorage senkronizasyonu
  useEffect(() => {
    if (storageKey) {
      localStorage.setItem(storageKey, JSON.stringify(state.present));
    }
  }, [state.present, storageKey]);

  const canUndo = state.past.length > 0;
  const canRedo = state.future.length > 0;

  const undo = useCallback(() => {
    setStateInternal((curr) => {
      if (curr.past.length === 0) return curr;

      const previous = curr.past[curr.past.length - 1];
      const newPast = curr.past.slice(0, curr.past.length - 1);

      return {
        past: newPast,
        present: previous,
        future: [curr.present, ...curr.future],
      };
    });
  }, []);

  const redo = useCallback(() => {
    setStateInternal((curr) => {
      if (curr.future.length === 0) return curr;

      const next = curr.future[0];
      const newFuture = curr.future.slice(1);

      return {
        past: [...curr.past, curr.present],
        present: next,
        future: newFuture,
      };
    });
  }, []);

  const setPresentState = useCallback((newState: T | ((prev: T) => T)) => {
    setStateInternal((curr) => {
      const resolved = typeof newState === 'function' 
        ? (newState as any)(curr.present) 
        : newState;
      
      // Eğer değer aynıysa işlem yapma (derin karşılaştırma gerekebilir ama şimdilik JSON.stringify yeterli)
      if (JSON.stringify(resolved) === JSON.stringify(curr.present)) return curr;

      if (debounceMs > 0) {
        // Zamanlayıcıyı temizle
        if (timerRef.current) clearTimeout(timerRef.current);

        // Eğer henüz debouncing başlamadıysa, şu anki durumu checkpoint olarak kaydet
        if (!pendingStateRef.current) {
          pendingStateRef.current = curr.present;
        }

        // Yeni bir zamanlayıcı başlat
        timerRef.current = setTimeout(() => {
          setStateInternal(latest => {
            if (!pendingStateRef.current) return latest;
            
            const newPast = [...latest.past, pendingStateRef.current].slice(-maxHistory);
            pendingStateRef.current = null;
            
            return {
              ...latest,
              past: newPast,
              future: []
            };
          });
          timerRef.current = null;
        }, debounceMs);

        return {
          ...curr,
          present: resolved,
          future: []
        };
      } else {
        const newPast = [...curr.past, curr.present].slice(-maxHistory);

        return {
          past: newPast,
          present: resolved,
          future: [],
        };
      }
    });
  }, [maxHistory, debounceMs]);

  // Bileşen unmount olduğunda timer'ı temizle
  useEffect(() => {
    return () => {
      if (timerRef.current) clearTimeout(timerRef.current);
    };
  }, []);

  const reset = useCallback((newInitialState: T) => {
    setStateInternal({ past: [], present: newInitialState, future: [] });
  }, []);

  return {
    state: state.present,
    setState: setPresentState,
    undo,
    redo,
    canUndo,
    canRedo,
    past: state.past,
    future: state.future,
    reset
  };
}
