/**
 * useInfiniteList — 서버가 한 번에 다 던진 배열을 클라이언트에서
 * 페이지 단위로 잘라 점진적으로 노출하는 훅.
 *
 * IntersectionObserver 로 sentinel(마지막 아이템 하단 또는 별도 div)이
 * 뷰포트에 들어올 때 다음 페이지를 노출.
 */
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';

interface UseInfiniteListOptions {
  /** 초기 노출 개수 (기본 10) */
  initialSize?: number;
  /** 한 번에 추가 노출할 개수 (기본 10) */
  pageSize?: number;
  /** sentinel 트리거 거리 (px). 음수면 미리 트리거. 기본 -100 */
  rootMargin?: string;
}

interface UseInfiniteListResult<T> {
  visible: T[];
  hasMore: boolean;
  sentinelRef: (node: Element | null) => void;
  reset: () => void;
}

export function useInfiniteList<T>(
  items: T[],
  { initialSize = 10, pageSize = 10, rootMargin = '200px' }: UseInfiniteListOptions = {},
): UseInfiniteListResult<T> {
  const [count, setCount] = useState(initialSize);
  const observerRef = useRef<IntersectionObserver | null>(null);

  // 입력 배열이 바뀌면 초기 size 로 리셋
  useEffect(() => {
    setCount(initialSize);
  }, [items, initialSize]);

  const visible = useMemo(() => items.slice(0, count), [items, count]);
  const hasMore = count < items.length;

  const sentinelRef = useCallback(
    (node: Element | null) => {
      if (observerRef.current) {
        observerRef.current.disconnect();
        observerRef.current = null;
      }
      if (!node || !hasMore) return;

      const observer = new IntersectionObserver(
        (entries) => {
          if (entries[0]?.isIntersecting) {
            setCount((prev) => Math.min(prev + pageSize, items.length));
          }
        },
        { rootMargin },
      );
      observer.observe(node);
      observerRef.current = observer;
    },
    [hasMore, items.length, pageSize, rootMargin],
  );

  const reset = useCallback(() => setCount(initialSize), [initialSize]);

  // unmount 정리
  useEffect(() => {
    return () => {
      observerRef.current?.disconnect();
    };
  }, []);

  return { visible, hasMore, sentinelRef, reset };
}
