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

interface VirtualListProps<T> {
    items: T[];
    itemHeight: number;
    overscan?: number;
    containerRef: React.RefObject<HTMLElement>;
    renderItem: (item: T, index: number) => React.ReactNode;
    getItemKey: (item: T, index: number) => string | number;
    getItemState?: (item: T, index: number) => unknown;
    animatePosition?: boolean;
    positionAnimationDuration?: number;
    debugName?: string;
    selectedFile?: string | null;
    selectedFiles?: string[];
    draggedPaths?: Set<string>; 
    isDragging?: boolean; 
}

interface VirtualItemProps<T> {
    item: T;
    index: number;
    top: number;
    height: number;
    renderItem: (item: T, index: number) => React.ReactNode;
    itemState?: unknown;
    animatePosition?: boolean;
    positionAnimationDuration?: number;
    debugName?: string;
    selectedFile?: string | null;
    selectedFiles?: string[];
    draggedPaths?: Set<string>;
    isDragging?: boolean;
}

const VirtualItemInner = <T,>({
    item,
    index,
    top,
    height,
    renderItem,
    animatePosition,
    positionAnimationDuration,
}: VirtualItemProps<T>) => {
    return (
        <div
            style={{
                position: 'absolute',
                top,
                left: 0,
                width: '100%',
                height,
                overflow: 'visible',
                pointerEvents: 'auto',
                transition: animatePosition ? `top ${positionAnimationDuration ?? 220}ms ease` : undefined,
                willChange: animatePosition ? 'top' : undefined,
                zIndex: 1,
            }}
        >
            {renderItem(item, index)}
        </div>
    );
};

const VirtualItem = memo(VirtualItemInner, (prev, next) => {
    return prev.item === next.item && 
           prev.index === next.index && 
           prev.top === next.top &&
           prev.height === next.height &&
           prev.itemState === next.itemState &&
           prev.renderItem === next.renderItem &&
           prev.isDragging === next.isDragging;
}) as typeof VirtualItemInner;

export function VirtualList<T>({
    items,
    itemHeight,
    overscan = 5,
    containerRef,
    renderItem,
    getItemKey,
    getItemState,
    animatePosition = false,
    positionAnimationDuration = 220,
    debugName = 'VirtualList',
    selectedFile,
    selectedFiles,
    draggedPaths,
    isDragging,
}: VirtualListProps<T>) {
    const [scrollTop, setScrollTop] = useState(0);
    const [containerHeight, setContainerHeight] = useState(0);
    const rafRef = useRef<number | null>(null);
    const prevRangeRef = useRef<{ start: number; end: number } | null>(null);

    useEffect(() => {
        const container = containerRef.current;
        if (!container) {
            return;
        }

        const updateHeight = () => {
            const height = container.clientHeight;
            setContainerHeight(prev => {
                if (prev !== height) {
                    return height;
                }
                return prev;
            });
        };

        updateHeight();

        const resizeObserver = new ResizeObserver(updateHeight);
        resizeObserver.observe(container);

        return () => {
            resizeObserver.disconnect();
        };
    }, [containerRef, debugName]);

    useEffect(() => {
        const container = containerRef.current;
        if (!container) return;

        const handleScroll = () => {
            if (rafRef.current !== null) return;
            rafRef.current = requestAnimationFrame(() => {
                rafRef.current = null;
                setScrollTop(container.scrollTop);
            });
        };

        container.addEventListener('scroll', handleScroll, { passive: true });
        return () => {
            container.removeEventListener('scroll', handleScroll);
            if (rafRef.current !== null) {
                cancelAnimationFrame(rafRef.current);
            }
        };
    }, [containerRef]);

    const { startIndex, endIndex, visibleItems } = useMemo(() => {
        const totalHeight = items.length * itemHeight;
        const start = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
        const visibleCount = Math.ceil(containerHeight / itemHeight);
        const end = Math.min(items.length - 1, start + visibleCount + overscan * 2);

        prevRangeRef.current = { start, end };

        return {
            startIndex: start,
            endIndex: end,
            visibleItems: items.slice(start, end + 1),
            totalHeight,
        };
    }, [items, itemHeight, scrollTop, containerHeight, overscan]);

    const totalHeight = items.length * itemHeight;

    return (
        <div style={{ 
            height: totalHeight, 
            width: '100%',
            position: 'relative',
            overflow: 'visible',
            pointerEvents: 'none' 
        }}>
            {visibleItems.map((item, i) => {
                const actualIndex = startIndex + i;
                return (
                    <VirtualItem
                        key={getItemKey(item, actualIndex)}
                        item={item}
                        index={actualIndex}
                        top={actualIndex * itemHeight}
                        height={itemHeight}
                        renderItem={renderItem}
                        itemState={getItemState?.(item, actualIndex)}
                        animatePosition={animatePosition}
                        positionAnimationDuration={positionAnimationDuration}
                        debugName={debugName}
                        selectedFile={selectedFile}
                        selectedFiles={selectedFiles}
                        draggedPaths={draggedPaths}
                        isDragging={isDragging}
                    />
                );
            })}
        </div>
    );
}

export default VirtualList;
