import { ReactNode, useEffect, useRef, useState } from "react";
import { Box } from "@mantine/core";

interface InfiniteScrollProps<T> {
  items: T[];
  renderItem: (item: T, index: number) => ReactNode;
  batchSize?: number;
  initialCount?: number;
  rootMargin?: string;
  loader?: ReactNode;
  hasMore?: boolean;
  loading?: boolean;
  onLoadMore?: () => void;
}

const InfiniteScroll = <T,>({
  items,
  renderItem,
  batchSize = 25,
  initialCount = batchSize,
  rootMargin = "600px 0px",
  loader,
  hasMore = false,
  loading = false,
  onLoadMore,
}: InfiniteScrollProps<T>) => {
  const [visibleCount, setVisibleCount] = useState(initialCount);
  const [prevItems, setPrevItems] = useState(items);
  const sentinelRef = useRef<HTMLDivElement>(null);

  if (prevItems !== items) {
    setPrevItems(items);
    setVisibleCount(initialCount);
  }

  const hasHiddenItems = visibleCount < items.length;
  const showLoader = hasHiddenItems || hasMore || loading;

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

    const observer = new IntersectionObserver(
      (entries) => {
        if (!entries.some((entry) => entry.isIntersecting)) return;

        if (visibleCount < items.length) {
          setVisibleCount((count) => Math.min(count + batchSize, items.length));
        } else if (hasMore && !loading) {
          onLoadMore?.();
        }
      },
      { rootMargin }
    );

    observer.observe(sentinel);
    return () => observer.disconnect();
  }, [items, visibleCount, batchSize, hasMore, loading, onLoadMore, rootMargin]);

  return (
    <>
      {items.slice(0, visibleCount).map((item, index) => renderItem(item, index))}
      {showLoader && (
        <>
          <Box ref={sentinelRef} />
          {loader}
        </>
      )}
    </>
  );
};

export default InfiniteScroll;
