import { useCallback, useMemo, useRef, useEffect, useState } from 'react';
import { useDebounce, useThrottle } from './useDebounce';

// Hook pour mémoriser les callbacks coûteux
export function useStableCallback<T extends (...args: any[]) => any>(
  callback: T,
  deps: React.DependencyList = []
): T {
  const callbackRef = useRef(callback);
  
  useEffect(() => {
    callbackRef.current = callback;
  }, [callback]);
  
  return useCallback(
    ((...args: any[]) => callbackRef.current(...args)) as T,
    deps
  );
}

// Hook pour mémoriser les valeurs coûteuses
export function useStableMemo<T>(
  factory: () => T,
  deps: React.DependencyList
): T {
  const factoryRef = useRef(factory);
  
  useEffect(() => {
    factoryRef.current = factory;
  }, [factory]);
  
  return useMemo(() => factoryRef.current(), deps);
}

// Hook pour éviter les re-renders inutiles
export function useShallowEqual<T>(value: T): T {
  const ref = useRef<T>(value);
  
  if (!shallowEqual(ref.current, value)) {
    ref.current = value;
  }
  
  return ref.current;
}

// Fonction utilitaire pour comparer les objets de manière superficielle
function shallowEqual(obj1: any, obj2: any): boolean {
  if (obj1 === obj2) return true;
  
  if (obj1 == null || obj2 == null) return false;
  
  if (typeof obj1 !== 'object' || typeof obj2 !== 'object') return false;
  
  const keys1 = Object.keys(obj1);
  const keys2 = Object.keys(obj2);
  
  if (keys1.length !== keys2.length) return false;
  
  for (const key of keys1) {
    if (obj1[key] !== obj2[key]) return false;
  }
  
  return true;
}

// Hook pour gérer les listes virtuelles avec performance
export function useVirtualList<T>(
  items: T[],
  itemHeight: number,
  containerHeight: number,
  overscan: number = 5
) {
  const [scrollTop, setScrollTop] = useState(0);
  const containerRef = useRef<HTMLDivElement>(null);
  
  const visibleRange = useMemo(() => {
    const startIndex = Math.floor(scrollTop / itemHeight);
    const endIndex = Math.min(
      startIndex + Math.ceil(containerHeight / itemHeight) + overscan,
      items.length - 1
    );
    
    return {
      startIndex: Math.max(0, startIndex - overscan),
      endIndex: Math.max(0, endIndex),
    };
  }, [scrollTop, itemHeight, containerHeight, overscan, items.length]);
  
  const visibleItems = useMemo(() => {
    return items.slice(visibleRange.startIndex, visibleRange.endIndex + 1);
  }, [items, visibleRange]);
  
  const totalHeight = items.length * itemHeight;
  const offsetY = visibleRange.startIndex * itemHeight;
  
  const handleScroll = useCallback((event: React.UIEvent<HTMLDivElement>) => {
    setScrollTop(event.currentTarget.scrollTop);
  }, []);
  
  return {
    containerRef,
    visibleItems,
    totalHeight,
    offsetY,
    handleScroll,
    visibleRange,
  };
}

// Hook pour gérer les états de chargement avec debounce
export function useLoadingState(initialState: boolean = false) {
  const [isLoading, setIsLoading] = useState(initialState);
  const [isLoadingDebounced, setIsLoadingDebounced] = useState(initialState);
  
  const debouncedSetLoading = useDebounce(setIsLoadingDebounced, 300);
  
  useEffect(() => {
    debouncedSetLoading(isLoading);
  }, [isLoading, debouncedSetLoading]);
  
  const setLoading = useCallback((loading: boolean) => {
    setIsLoading(loading);
  }, []);
  
  return {
    isLoading,
    isLoadingDebounced,
    setLoading,
  };
}

// Hook pour gérer les erreurs avec retry
export function useErrorWithRetry<T>(
  initialData: T | null = null,
  maxRetries: number = 3
) {
  const [data, setData] = useState<T | null>(initialData);
  const [error, setError] = useState<Error | null>(null);
  const [retryCount, setRetryCount] = useState(0);
  const [isRetrying, setIsRetrying] = useState(false);
  
  const retry = useCallback(async (asyncFn: () => Promise<T>) => {
    if (retryCount >= maxRetries) {
      setError(new Error(`Max retries (${maxRetries}) exceeded`));
      return;
    }
    
    setIsRetrying(true);
    setError(null);
    
    try {
      const result = await asyncFn();
      setData(result);
      setRetryCount(0);
    } catch (err) {
      setError(err as Error);
      setRetryCount(prev => prev + 1);
    } finally {
      setIsRetrying(false);
    }
  }, [retryCount, maxRetries]);
  
  const reset = useCallback(() => {
    setError(null);
    setRetryCount(0);
    setIsRetrying(false);
  }, []);
  
  return {
    data,
    error,
    retryCount,
    isRetrying,
    retry,
    reset,
    canRetry: retryCount < maxRetries,
  };
}

// Hook pour gérer les intersections avec performance
export function useIntersectionObserver(
  options: IntersectionObserverInit = {}
) {
  const [isIntersecting, setIsIntersecting] = useState(false);
  const [hasIntersected, setHasIntersected] = useState(false);
  const ref = useRef<HTMLElement>(null);
  
  useEffect(() => {
    const element = ref.current;
    if (!element) return;
    
    const observer = new IntersectionObserver(
      ([entry]) => {
        setIsIntersecting(entry.isIntersecting);
        if (entry.isIntersecting && !hasIntersected) {
          setHasIntersected(true);
        }
      },
      options
    );
    
    observer.observe(element);
    
    return () => {
      observer.unobserve(element);
    };
  }, [options, hasIntersected]);
  
  return { ref, isIntersecting, hasIntersected };
}

// Hook pour gérer les dimensions avec performance
export function useElementSize() {
  const [size, setSize] = useState({ width: 0, height: 0 });
  const ref = useRef<HTMLElement>(null);
  
  useEffect(() => {
    const element = ref.current;
    if (!element) return;
    
    const resizeObserver = new ResizeObserver(entries => {
      const entry = entries[0];
      if (entry) {
        const { width, height } = entry.contentRect;
        setSize({ width, height });
      }
    });
    
    resizeObserver.observe(element);
    
    return () => {
      resizeObserver.disconnect();
    };
  }, []);
  
  return { ref, size };
}

// Hook pour gérer les animations avec performance
export function useAnimationFrame(callback: () => void, deps: React.DependencyList = []) {
  const callbackRef = useRef(callback);
  
  useEffect(() => {
    callbackRef.current = callback;
  }, [callback]);
  
  useEffect(() => {
    let animationId: number;
    
    const animate = () => {
      callbackRef.current();
      animationId = requestAnimationFrame(animate);
    };
    
    animationId = requestAnimationFrame(animate);
    
    return () => {
      cancelAnimationFrame(animationId);
    };
  }, deps);
}

// Hook pour gérer les timers avec performance
export function useInterval(callback: () => void, delay: number | null) {
  const callbackRef = useRef(callback);
  
  useEffect(() => {
    callbackRef.current = callback;
  }, [callback]);
  
  useEffect(() => {
    if (delay === null) return;
    
    const intervalId = setInterval(() => {
      callbackRef.current();
    }, delay);
    
    return () => {
      clearInterval(intervalId);
    };
  }, [delay]);
}

// Hook pour gérer les timeouts avec performance
export function useTimeout(callback: () => void, delay: number | null) {
  const callbackRef = useRef(callback);
  
  useEffect(() => {
    callbackRef.current = callback;
  }, [callback]);
  
  useEffect(() => {
    if (delay === null) return;
    
    const timeoutId = setTimeout(() => {
      callbackRef.current();
    }, delay);
    
    return () => {
      clearTimeout(timeoutId);
    };
  }, [delay]);
}

// Hook pour gérer les états complexes avec performance
export function useComplexState<T>(
  initialState: T,
  reducer: (state: T, action: any) => T
) {
  const [state, setState] = useState(initialState);
  
  const dispatch = useCallback((action: any) => {
    setState(prevState => reducer(prevState, action));
  }, [reducer]);
  
  return [state, dispatch] as const;
}

// Hook pour gérer les refs avec performance
export function useRefs<T = HTMLElement>() {
  const refs = useRef<Map<string, T>>(new Map());
  
  const getRef = useCallback((key: string) => {
    return refs.current.get(key);
  }, []);
  
  const setRef = useCallback((key: string, element: T | null) => {
    if (element) {
      refs.current.set(key, element);
    } else {
      refs.current.delete(key);
    }
  }, []);
  
  return { getRef, setRef };
}

// Hook pour gérer les événements avec performance
export function useEventListener<T extends keyof WindowEventMap>(
  eventName: T,
  handler: (event: WindowEventMap[T]) => void,
  element: Window | HTMLElement | null = window,
  options?: AddEventListenerOptions
) {
  const handlerRef = useRef(handler);
  
  useEffect(() => {
    handlerRef.current = handler;
  }, [handler]);
  
  useEffect(() => {
    if (!element) return;
    
    const eventListener = (event: Event) => {
      handlerRef.current(event as WindowEventMap[T]);
    };
    
    element.addEventListener(eventName, eventListener, options);
    
    return () => {
      element.removeEventListener(eventName, eventListener, options);
    };
  }, [eventName, element, options]);
}

// Hook pour gérer les états persistants avec performance
export function usePersistentState<T>(
  key: string,
  initialState: T,
  storage: Storage = localStorage
) {
  const [state, setState] = useState<T>(() => {
    try {
      const item = storage.getItem(key);
      return item ? JSON.parse(item) : initialState;
    } catch {
      return initialState;
    }
  });
  
  const setPersistentState = useCallback((newState: T | ((prev: T) => T)) => {
    setState(prevState => {
      const nextState = typeof newState === 'function' 
        ? (newState as (prev: T) => T)(prevState)
        : newState;
      
      try {
        storage.setItem(key, JSON.stringify(nextState));
      } catch {
        console.warn(`Failed to persist state for key: ${key}`);
      }
      
      return nextState;
    });
  }, [key, storage]);
  
  return [state, setPersistentState] as const;
}
