import { useState, useEffect, useCallback } from 'react';
import { logger } from '@/utils/logger';

/**
 * Hook pour précharger les routes
 * @param routePath - Le chemin de la route à précharger
 * @param delay - Délai avant le préchargement (ms)
 */
export function useRoutePreload(routePath: string, delay: number = 0) {
  const [isPreloading, setIsPreloading] = useState(false);
  const [isPreloaded, setIsPreloaded] = useState(false);

  const preloadRoute = useCallback(async () => {
    if (isPreloaded) return;

    setIsPreloading(true);

    try {
      // Simuler le préchargement de la route
      await new Promise((resolve) => setTimeout(resolve, delay));

      // Ici, vous pourriez importer dynamiquement le composant
      // const component = await import(`@/pages${routePath}`);

      setIsPreloaded(true);
    } catch (error) {
      logger.error('Error preloading route', {
        error: error instanceof Error ? error.message : String(error),
        stack: error instanceof Error ? error.stack : undefined,
        routePath,
      });
    } finally {
      setIsPreloading(false);
    }
  }, [routePath, delay, isPreloaded]);

  return { preloadRoute, isPreloading, isPreloaded };
}

/**
 * Hook pour gérer les performances et optimisations
 * @returns Fonctions d'optimisation
 */
export function usePerformanceOptimization() {
  const [isVisible, setIsVisible] = useState(true);

  useEffect(() => {
    const handleVisibilityChange = () => {
      setIsVisible(!document.hidden);
    };

    document.addEventListener('visibilitychange', handleVisibilityChange);

    return () => {
      document.removeEventListener('visibilitychange', handleVisibilityChange);
    };
  }, []);

  const optimizeForVisibility = useCallback(
    (callback: () => void) => {
      if (isVisible) {
        callback();
      }
    },
    [isVisible],
  );

  const throttle = useCallback(
    <T extends (...args: any[]) => any>(func: T, delay: number): T => {
      let timeoutId: NodeJS.Timeout | null = null;
      let lastExecTime = 0;

      return ((...args: Parameters<T>) => {
        const currentTime = Date.now();

        if (currentTime - lastExecTime > delay) {
          func(...args);
          lastExecTime = currentTime;
        } else {
          if (timeoutId) {
            clearTimeout(timeoutId);
          }
          timeoutId = setTimeout(
            () => {
              func(...args);
              lastExecTime = Date.now();
            },
            delay - (currentTime - lastExecTime),
          );
        }
      }) as T;
    },
    [],
  );

  const debounce = useCallback(
    <T extends (...args: any[]) => any>(func: T, delay: number): T => {
      let timeoutId: NodeJS.Timeout | null = null;

      return ((...args: Parameters<T>) => {
        if (timeoutId) {
          clearTimeout(timeoutId);
        }
        timeoutId = setTimeout(() => func(...args), delay);
      }) as T;
    },
    [],
  );

  return {
    isVisible,
    optimizeForVisibility,
    throttle,
    debounce,
  };
}

/**
 * Hook pour gérer les erreurs de manière centralisée
 * @param onError - Callback appelé en cas d'erreur
 * @returns Fonctions de gestion d'erreur
 */
export function useErrorHandler(
  onError?: (error: Error, errorInfo?: any) => void,
) {
  const [error, setError] = useState<Error | null>(null);

  const handleError = useCallback(
    (error: Error, errorInfo?: any) => {
      setError(error);
      onError?.(error, errorInfo);

      // Log l'erreur pour le monitoring
      logger.error('Error caught by useErrorHandler', {
        error: error.message,
        stack: error.stack,
        errorInfo,
      });
    },
    [onError],
  );

  const clearError = useCallback(() => {
    setError(null);
  }, []);

  const withErrorHandling = useCallback(
    <T extends (...args: any[]) => any>(fn: T): T => {
      return ((...args: Parameters<T>) => {
        try {
          const result = fn(...args);

          // Si c'est une Promise, gérer les erreurs asynchrones
          if (result instanceof Promise) {
            return result.catch((error) => {
              handleError(error);
              throw error;
            });
          }

          return result;
        } catch (error) {
          handleError(error as Error);
          throw error;
        }
      }) as T;
    },
    [handleError],
  );

  return {
    error,
    handleError,
    clearError,
    withErrorHandling,
  };
}

/**
 * Hook pour gérer les états de chargement
 * @param initialState - État initial
 * @returns Fonctions de gestion des états
 */
export function useLoadingState(initialState: boolean = false) {
  const [isLoading, setIsLoading] = useState(initialState);
  const [loadingStates, setLoadingStates] = useState<Record<string, boolean>>(
    {},
  );

  const setLoading = useCallback((loading: boolean) => {
    setIsLoading(loading);
  }, []);

  const setLoadingFor = useCallback((key: string, loading: boolean) => {
    setLoadingStates((prev) => ({
      ...prev,
      [key]: loading,
    }));
  }, []);

  const isLoadingFor = useCallback(
    (key: string) => {
      return loadingStates[key] || false;
    },
    [loadingStates],
  );

  const withLoading = useCallback(
    async <T>(asyncFn: () => Promise<T>, key?: string): Promise<T> => {
      try {
        if (key) {
          setLoadingFor(key, true);
        } else {
          setLoading(true);
        }

        const result = await asyncFn();
        return result;
      } finally {
        if (key) {
          setLoadingFor(key, false);
        } else {
          setLoading(false);
        }
      }
    },
    [setLoading, setLoadingFor],
  );

  return {
    isLoading,
    loadingStates,
    setLoading,
    setLoadingFor,
    isLoadingFor,
    withLoading,
  };
}
