import React, { useRef, useEffect, useState, useCallback } from 'react';
import { useVirtualizer } from '@tanstack/react-virtual';

export interface VirtualizedListProps<T> {
  items: T[];
  itemHeight: number;
  containerHeight: number;
  renderItem: (item: T, index: number) => React.ReactNode;
  className?: string;
  overscan?: number;
  onScroll?: (scrollTop: number) => void;
  onItemsRendered?: (startIndex: number, endIndex: number) => void;
}

export const VirtualizedList = React.forwardRef<
  HTMLDivElement,
  VirtualizedListProps<any>
>((props, ref) => {
  const {
    items,
    itemHeight,
    containerHeight,
    renderItem,
    className = '',
    overscan = 5,
    onScroll,
    onItemsRendered,
  } = props;

  const internalRef = useRef<HTMLDivElement>(null);
  // Use forwarded ref if available, otherwise internal fallback
  // This is a simple merge strategy: we need the ref internally for virtualizer
  // So we'll assign to both if forwarded ref is object, or call if function.
  // Actually, easiest is to just use one ref and sync or useImperativeHandle.
  // But virtualizer needs a RefObject.

  // Let's use internalRef as primary and expose it via useImperativeHandle or sync.
  React.useImperativeHandle(ref, () => internalRef.current as HTMLDivElement);

  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  const [_isScrolling, setIsScrolling] = useState(false);
  const scrollOffsetRef = useRef(0);
  const scrollTimeoutRef = useRef<NodeJS.Timeout | null>(null);

  const virtualizer = useVirtualizer({
    count: items.length,
    getScrollElement: () => internalRef.current,
    estimateSize: () => itemHeight,
    overscan,
  });

  const virtualItems = virtualizer.getVirtualItems();

  // Handle scroll events with debouncing
  const handleScroll = useCallback(() => {
    const scrollTop = internalRef.current?.scrollTop || 0;
    // Check if scrolling (value not used but calculation needed for side effect)
    Math.abs(scrollTop - (scrollOffsetRef.current || 0)) > 0;

    setIsScrolling(true); // Keep this to trigger the debounced state

    if (scrollTimeoutRef.current) {
      clearTimeout(scrollTimeoutRef.current);
    }

    scrollTimeoutRef.current = setTimeout(() => {
      setIsScrolling(false);
    }, 150);

    scrollOffsetRef.current = scrollTop; // Update scroll offset

    if (onScroll && internalRef.current) {
      onScroll(internalRef.current.scrollTop);
    }

    if (onItemsRendered && virtualItems.length > 0) {
      const startIndex = Math.max(
        0,
        Math.floor(scrollTop / itemHeight) - overscan,
      );
      const endIndex = virtualItems[virtualItems.length - 1].index;
      onItemsRendered(startIndex, endIndex);
    }
  }, [onScroll, onItemsRendered, virtualItems, itemHeight, overscan]); // Added itemHeight, overscan to dependencies

  useEffect(() => {
    const scrollElement = internalRef.current;
    if (scrollElement) {
      scrollElement.addEventListener('scroll', handleScroll, { passive: true });
      return () => scrollElement.removeEventListener('scroll', handleScroll);
    }
    return undefined;
  }, [handleScroll]);

  // Cleanup timeout on unmount
  useEffect(() => {
    return () => {
      if (scrollTimeoutRef.current) {
        clearTimeout(scrollTimeoutRef.current);
      }
    };
  }, []);

  const totalSize = virtualizer.getTotalSize();
  const paddingTop = virtualItems.length > 0 ? virtualItems[0]?.start || 0 : 0;
  const paddingBottom =
    totalSize -
    (virtualItems.length > 0
      ? virtualItems[virtualItems.length - 1]?.end || 0
      : 0);

  return (
    <div
      ref={internalRef}
      className={`overflow-auto ${className}`}
      style={{ height: containerHeight }}
    >
      <div
        style={{
          height: totalSize,
          width: '100%',
          position: 'relative',
        }}
      >
        {paddingTop > 0 && <div style={{ height: paddingTop }} />}
        {virtualItems.map((virtualItem) => (
          <div
            key={virtualItem.key}
            data-index={virtualItem.index}
            ref={virtualizer.measureElement}
            style={{
              position: 'absolute',
              top: 0,
              left: 0,
              width: '100%',
              transform: `translateY(${virtualItem.start}px)`,
            }}
          >
            {renderItem(items[virtualItem.index], virtualItem.index)}
          </div>
        ))}
        {paddingBottom > 0 && <div style={{ height: paddingBottom }} />}
      </div>
    </div>
  );
}) as <T>(
  props: VirtualizedListProps<T> & { ref?: React.Ref<HTMLDivElement> },
) => React.ReactElement;

// Hook for infinite scrolling
export function useInfiniteScroll<T>(
  items: T[],
  hasNextPage: boolean,
  isFetching: boolean,
  fetchNextPage: () => void,
  threshold: number = 5,
) {
  const [isNearBottom, setIsNearBottom] = useState(false);

  const handleItemsRendered = useCallback(
    (_startIndex: number, endIndex: number) => {
      const isNearEnd = endIndex >= items.length - threshold;
      setIsNearBottom(isNearEnd);
    },
    [items.length, threshold],
  );

  useEffect(() => {
    if (isNearBottom && hasNextPage && !isFetching) {
      fetchNextPage();
    }
  }, [isNearBottom, hasNextPage, isFetching, fetchNextPage]);

  return { handleItemsRendered };
}

// Hook for scroll position restoration
export function useScrollPosition(key: string) {
  const [scrollPosition, setScrollPosition] = useState(0);

  useEffect(() => {
    const saved = sessionStorage.getItem(`scroll-${key}`);
    if (saved) {
      setScrollPosition(parseInt(saved, 10));
    }
  }, [key]);

  const saveScrollPosition = useCallback(
    (position: number) => {
      setScrollPosition(position);
      sessionStorage.setItem(`scroll-${key}`, position.toString());
    },
    [key],
  );

  const restoreScrollPosition = useCallback(
    (element: HTMLElement | null) => {
      if (element && scrollPosition > 0) {
        element.scrollTop = scrollPosition;
      }
    },
    [scrollPosition],
  );

  return { scrollPosition, saveScrollPosition, restoreScrollPosition };
}
