/**
 * useInfiniteScroll — auto-paginate a scrollable container.
 *
 * FEATURE (2026-01) — replaces the explicit "Load More" button on
 * Movies / Series / Live TV with IntersectionObserver-driven pagination.
 *
 * Usage
 * -----
 *   const { sentinelRef } = useInfiniteScroll({
 *     hasMore,
 *     onLoadMore: () => setVisibleCount((c) => c + PAGE_SIZE),
 *   });
 *   …
 *   <div ref={sentinelRef} />   // placed at the END of the scrollable list
 *
 * Implementation notes
 * --------------------
 * • Observer roots itself at the document viewport (`root: null`).
 *   IntersectionObserver still walks ancestor scroll containers
 *   transparently when reporting whether the sentinel is "in view",
 *   so this works correctly for grids inside a FocusableSection's
 *   `overflow-y: auto` scroll-port AS LONG AS that scroll-port is
 *   itself roughly viewport-height (which is the case for every
 *   page we mount through PageLayout). For deeply-nested or much-
 *   taller-than-viewport scroll containers, pass a custom `rootRef`
 *   to scope the observer (future enhancement, not needed today).
 * • Fires `onLoadMore` as soon as the sentinel becomes visible, with
 *   a 200px lookahead so the next page is fetched BEFORE the user
 *   reaches the bottom (smooth, no flicker).
 * • Re-arms automatically each time `hasMore` flips back to true (e.g.
 *   when the user changes search / category and pagination resets).
 */
import { useEffect, useRef } from "react";

export type UseInfiniteScrollOptions = {
  /** True while more pages are available. When false the observer is
   *  disconnected and `onLoadMore` is never called. */
  hasMore: boolean;
  /** Called whenever the sentinel becomes visible. Idempotent — the
   *  observer keeps firing on subsequent intersections so consumers
   *  must bump their own counter / increment internally. */
  onLoadMore: () => void;
  /** Pixels of "lookahead" — fires earlier so users never see a stall.
   *  Default 200. Set to 0 for "only when the sentinel touches the
   *  bottom edge". */
  rootMargin?: number;
};

export function useInfiniteScroll({
  hasMore,
  onLoadMore,
  rootMargin = 200,
}: UseInfiniteScrollOptions) {
  const sentinelRef = useRef<HTMLDivElement | null>(null);

  useEffect(() => {
    const node = sentinelRef.current;
    if (!node || !hasMore) return;
    // The default null root means "viewport" — that's fine for grids
    // mounted directly inside PageLayout because the actual scrolling
    // container is the FocusableSection (overflow-y: auto). The
    // observer still fires correctly because IntersectionObserver
    // walks ancestor scroll roots transparently.
    const observer = new IntersectionObserver(
      (entries) => {
        for (const entry of entries) {
          if (entry.isIntersecting) {
            onLoadMore();
            break;
          }
        }
      },
      { root: null, rootMargin: `${rootMargin}px`, threshold: 0 },
    );
    observer.observe(node);
    return () => observer.disconnect();
  }, [hasMore, onLoadMore, rootMargin]);

  return { sentinelRef };
}
