/**
 * Hook useInfiniteScroll
 * Détecte le scroll et charge automatiquement plus de contenu en utilisant Intersection Observer
 */

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

export interface UseInfiniteScrollOptions {
  /**
   * Callback appelé quand on atteint le seuil de scroll
   */
  onLoadMore: () => void | Promise<void>;
  /**
   * Si le chargement est en cours
   */
  isLoading?: boolean;
  /**
   * Si on a atteint la fin (plus de contenu à charger)
   */
  hasMore?: boolean;
  /**
   * Seuil de déclenchement (en pixels ou ratio)
   * Par défaut: 100px avant la fin
   */
  threshold?: number;
  /**
   * Root margin pour l'Intersection Observer
   * Par défaut: "100px"
   */
  rootMargin?: string;
  /**
   * Élément root pour l'Intersection Observer
   * Par défaut: viewport
   */
  root?: Element | null;
  /**
   * Désactiver le hook
   */
  disabled?: boolean;
}

export interface UseInfiniteScrollReturn {
  /**
   * Ref à attacher à l'élément sentinelle (élément de déclenchement)
   */
  sentinelRef: RefObject<HTMLDivElement>;
  /**
   * Ref à attacher au conteneur scrollable (optionnel)
   */
  containerRef: RefObject<HTMLDivElement>;
}

/**
 * Hook pour implémenter le scroll infini
 * Utilise Intersection Observer pour détecter quand l'utilisateur approche de la fin
 */
export function useInfiniteScroll({
  onLoadMore,
  isLoading = false,
  hasMore = true,
  threshold = 100,
  rootMargin,
  root = null,
  disabled = false,
}: UseInfiniteScrollOptions): UseInfiniteScrollReturn {
  const sentinelRef = useRef<HTMLDivElement>(null);
  const containerRef = useRef<HTMLDivElement>(null);
  const observerRef = useRef<IntersectionObserver | null>(null);
  const isLoadingRef = useRef(false);

  // Mettre à jour la ref pour éviter les closures
  useEffect(() => {
    isLoadingRef.current = isLoading;
  }, [isLoading]);

  // Callback pour charger plus de contenu
  const handleLoadMore = useCallback(async () => {
    // Éviter les appels multiples simultanés
    if (isLoadingRef.current || !hasMore || disabled) {
      return;
    }

    isLoadingRef.current = true;
    try {
      await onLoadMore();
    } catch (error) {
      logger.error('Error loading more content:', { error });
    } finally {
      // Laisser un petit délai avant de permettre un nouveau chargement
      setTimeout(() => {
        isLoadingRef.current = false;
      }, 100);
    }
  }, [onLoadMore, hasMore, disabled]);

  // Configuration de l'Intersection Observer
  useEffect(() => {
    if (disabled || !hasMore) {
      // Nettoyer l'observer si désactivé ou plus de contenu
      if (observerRef.current) {
        observerRef.current.disconnect();
        observerRef.current = null;
      }
      return;
    }

    const sentinel = sentinelRef.current;
    if (!sentinel) {
      return;
    }

    // Calculer rootMargin si non fourni
    const margin = rootMargin || `${threshold}px`;

    // Créer l'observer
    observerRef.current = new IntersectionObserver(
      (entries) => {
        const [entry] = entries;
        // Si l'élément sentinelle est visible et qu'on n'est pas déjà en train de charger
        if (entry.isIntersecting && !isLoadingRef.current && hasMore) {
          handleLoadMore();
        }
      },
      {
        root: root || containerRef.current,
        rootMargin: margin,
        threshold: 0.1, // Déclencher quand 10% de l'élément est visible
      },
    );

    // Observer l'élément sentinelle
    observerRef.current.observe(sentinel);

    // Cleanup
    return () => {
      if (observerRef.current) {
        observerRef.current.disconnect();
        observerRef.current = null;
      }
    };
  }, [disabled, hasMore, handleLoadMore, root, rootMargin, threshold]);

  return {
    sentinelRef,
    containerRef,
  };
}

export default useInfiniteScroll;
