/**
 * Liste virtualisée pour les grandes quantités de données
 */

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

export interface VirtualizedListProps<T> {
  items: T[];
  itemHeight: number;
  containerHeight: number;
  renderItem: (item: T, index: number) => React.ReactNode;
  className?: string;
  loading?: boolean;
  loadingItemCount?: number;
  onScrollEnd?: () => void;
  overscan?: number; // Nombre d'éléments supplémentaires à rendre
}

export function VirtualizedList<T>({
  items,
  itemHeight,
  containerHeight,
  renderItem,
  className = "",
  loading = false,
  loadingItemCount = 5,
  onScrollEnd,
  overscan = 5
}: VirtualizedListProps<T>) {
  const [scrollTop, setScrollTop] = useState(0);
  const scrollElementRef = useRef<HTMLDivElement>(null);

  // Calculer les éléments visibles
  const visibleRange = useMemo(() => {
    const visibleStart = Math.floor(scrollTop / itemHeight);
    const visibleEnd = Math.min(
      visibleStart + Math.ceil(containerHeight / itemHeight),
      items.length - 1
    );

    return {
      start: Math.max(0, visibleStart - overscan),
      end: Math.min(items.length - 1, visibleEnd + overscan)
    };
  }, [scrollTop, itemHeight, containerHeight, items.length, overscan]);

  // Éléments à rendre
  const visibleItems = useMemo(() => {
    const itemsToRender = [];
    
    for (let i = visibleRange.start; i <= visibleRange.end; i++) {
      if (items[i]) {
        itemsToRender.push({
          index: i,
          item: items[i],
          style: {
            position: 'absolute' as const,
            top: i * itemHeight,
            left: 0,
            right: 0,
            height: itemHeight
          }
        });
      }
    }
    
    return itemsToRender;
  }, [items, visibleRange, itemHeight]);

  // Gestionnaire de scroll optimisé
  const handleScroll = useCallback((e: React.UIEvent<HTMLDivElement>) => {
    const newScrollTop = e.currentTarget.scrollTop;
    setScrollTop(newScrollTop);

    // Détecter la fin du scroll
    if (onScrollEnd) {
      const { scrollHeight, clientHeight } = e.currentTarget;
      const isNearEnd = newScrollTop + clientHeight >= scrollHeight - 100;
      
      if (isNearEnd) {
        onScrollEnd();
      }
    }
  }, [onScrollEnd]);

  // Rendu des éléments de chargement
  const renderLoadingItems = useCallback(() => {
    if (!loading) return null;

    return Array.from({ length: loadingItemCount }, (_, index) => (
      <div
        key={`loading-${index}`}
        className="absolute left-0 right-0 flex items-center p-4 animate-pulse"
        style={{
          top: (items.length + index) * itemHeight,
          height: itemHeight
        }}
      >
        <div className="flex-1 space-y-2">
          <div className="h-4 bg-gray-200 rounded w-3/4"></div>
          <div className="h-3 bg-gray-200 rounded w-1/2"></div>
        </div>
      </div>
    ));
  }, [loading, loadingItemCount, items.length, itemHeight]);

  const totalHeight = items.length * itemHeight + (loading ? loadingItemCount * itemHeight : 0);

  return (
    <div
      ref={scrollElementRef}
      className={`overflow-auto ${className}`}
      style={{ height: containerHeight }}
      onScroll={handleScroll}
      data-testid="virtualized-list"
    >
      <div
        style={{ height: totalHeight, position: 'relative' }}
      >
        {/* Éléments visibles */}
        {visibleItems.map(({ index, item, style }) => (
          <div key={index} style={style}>
            {renderItem(item, index)}
          </div>
        ))}
        
        {/* Éléments de chargement */}
        {renderLoadingItems()}
      </div>
    </div>
  );
}

// Version mémorisée par défaut
export default memo(VirtualizedList) as typeof VirtualizedList;

// Hook pour gérer le chargement infini
export function useInfiniteList<T>({
  fetchData,
  pageSize = 20,
  initialData = []
}: {
  fetchData: (page: number, pageSize: number) => Promise<T[]>;
  pageSize?: number;
  initialData?: T[];
}) {
  const [items, setItems] = useState<T[]>(initialData);
  const [loading, setLoading] = useState(false);
  const [hasMore, setHasMore] = useState(true);
  const [page, setPage] = useState(1);

  const loadMore = useCallback(async () => {
    if (loading || !hasMore) return;

    setLoading(true);
    try {
      const newItems = await fetchData(page, pageSize);
      
      if (newItems.length === 0) {
        setHasMore(false);
      } else {
        setItems(prev => [...prev, ...newItems]);
        setPage(prev => prev + 1);
      }
    } catch (error) {
      console.error('Erreur lors du chargement des données:', error);
    } finally {
      setLoading(false);
    }
  }, [fetchData, page, pageSize, loading, hasMore]);

  const reset = useCallback(() => {
    setItems(initialData);
    setPage(1);
    setHasMore(true);
    setLoading(false);
  }, [initialData]);

  return {
    items,
    loading,
    hasMore,
    loadMore,
    reset
  };
}