import { useEffect, useRef, type ReactNode, type RefObject } from 'react';
import styles from './InfiniteScrollArea.module.css';

export interface InfiniteScrollAreaProps {
  onEndReached: () => void;
  onTopVisibilityChange?: (atTop: boolean) => void;
  isFetchingNextPage: boolean;
  hasNextPage: boolean;
  isLoading?: boolean;
  hasItems?: boolean;
  maxHeight?: number | string;
  children: ReactNode;
  /** Optional caller-owned scroll container ref (e.g. for scroll-to-top on refresh). */
  scrollRef?: RefObject<HTMLDivElement | null>;
  className?: string;
}

export function InfiniteScrollArea({
  onEndReached,
  onTopVisibilityChange,
  isFetchingNextPage,
  hasNextPage,
  isLoading = false,
  hasItems = true,
  maxHeight = 360,
  children,
  scrollRef,
  className,
}: InfiniteScrollAreaProps) {
  const internalRef = useRef<HTMLDivElement | null>(null);
  const containerRef = scrollRef ?? internalRef;
  const topSentinel = useRef<HTMLDivElement | null>(null);
  const bottomSentinel = useRef<HTMLDivElement | null>(null);

  useEffect(() => {
    if (!onTopVisibilityChange) return;
    const root = containerRef.current;
    const target = topSentinel.current;
    if (!root || !target) return;
    const obs = new IntersectionObserver(
      (entries) => {
        for (const e of entries) onTopVisibilityChange(e.isIntersecting);
      },
      { root, threshold: 1.0 },
    );
    obs.observe(target);
    return () => obs.disconnect();
  }, [containerRef, onTopVisibilityChange]);

  useEffect(() => {
    if (!hasNextPage) return;
    const root = containerRef.current;
    const target = bottomSentinel.current;
    if (!root || !target) return;
    const obs = new IntersectionObserver(
      (entries) => {
        for (const e of entries) if (e.isIntersecting) onEndReached();
      },
      { root, rootMargin: '100px', threshold: 0 },
    );
    obs.observe(target);
    return () => obs.disconnect();
  }, [containerRef, onEndReached, hasNextPage]);

  return (
    <div
      ref={containerRef}
      className={`${styles.scrollArea}${className ? ` ${className}` : ''}`}
      style={{ maxHeight }}
    >
      <div ref={topSentinel} className={styles.sentinel} aria-hidden="true" />
      {children}
      <div ref={bottomSentinel} className={styles.sentinel} aria-hidden="true" />
      {isFetchingNextPage && <div className={styles.loadingMore}>Loading more…</div>}
      {!hasNextPage && !isLoading && hasItems && (
        <div className={styles.endOfStream}>End of stream</div>
      )}
    </div>
  );
}
