import { useEffect, useMemo, useRef, useState, type Key, type ReactNode } from "react";
import { useVirtualizer } from "@tanstack/react-virtual";

interface VirtualizedGridProps<T> {
  items: T[];
  className?: string;
  minColumnWidth: number;
  maxColumnWidth?: number;
  gap?: number;
  overscan?: number;
  itemAspectRatio?: number;
  empty?: ReactNode;
  itemKey?: (item: T, index: number) => Key;
  renderItem: (item: T, index: number) => ReactNode;
}

const fallbackWidth = 720;

export const VirtualizedGrid = <T,>({
  items,
  className,
  minColumnWidth,
  maxColumnWidth,
  gap = 8,
  overscan = 3,
  itemAspectRatio = 0.86,
  empty = null,
  itemKey,
  renderItem
}: VirtualizedGridProps<T>) => {
  const scrollRef = useRef<HTMLDivElement | null>(null);
  const [width, setWidth] = useState(fallbackWidth);

  useEffect(() => {
    const element = scrollRef.current;
    if (!element || typeof ResizeObserver === "undefined") {
      return;
    }

    const measure = () => setWidth(Math.max(element.clientWidth, minColumnWidth));
    measure();
    const observer = new ResizeObserver(() => measure());
    observer.observe(element);
    return () => observer.disconnect();
  }, [minColumnWidth]);

  const columns = Math.max(1, Math.floor((width + gap) / (minColumnWidth + gap)));
  const rowCount = Math.ceil(items.length / columns);
  const computedCellWidth = Math.max(minColumnWidth, (width - gap * Math.max(columns - 1, 0)) / columns);
  const cellWidth = maxColumnWidth ? Math.min(computedCellWidth, maxColumnWidth) : computedCellWidth;
  const rowHeight = Math.max(1, cellWidth / itemAspectRatio);

  const rows = useMemo(() => {
    const grouped: T[][] = [];
    for (let index = 0; index < items.length; index += columns) {
      grouped.push(items.slice(index, index + columns));
    }
    return grouped;
  }, [columns, items]);

  const virtualizer = useVirtualizer({
    count: rowCount,
    getScrollElement: () => scrollRef.current,
    estimateSize: () => rowHeight + gap,
    overscan
  });

  if (items.length === 0) {
    return <div className={className}>{empty}</div>;
  }

  return (
    <div ref={scrollRef} className={className}>
      <div style={{ height: `${virtualizer.getTotalSize()}px`, position: "relative" }}>
        {virtualizer.getVirtualItems().map((virtualRow) => {
          const row = rows[virtualRow.index] ?? [];
          const startIndex = virtualRow.index * columns;
          return (
            <div
              key={virtualRow.key}
              style={{
                position: "absolute",
                top: 0,
                left: 0,
                width: "100%",
                display: "grid",
                gap: `${gap}px`,
                justifyContent: "start",
                gridTemplateColumns: `repeat(${columns}, ${cellWidth}px)`,
                transform: `translateY(${virtualRow.start}px)`
              }}
            >
              {row.map((item, offset) => (
                <div key={itemKey ? itemKey(item, startIndex + offset) : `${virtualRow.index}-${offset}`}>
                  {renderItem(item, startIndex + offset)}
                </div>
              ))}
            </div>
          );
        })}
      </div>
    </div>
  );
};
