import { RefObject, useEffect, useRef } from 'react';

const FALLBACK_DELAY_MS = 250;

/**
 * Calls `callback` once after a scroll gesture has fully settled.
 *
 * Uses the native `scrollend` event when available (Chrome 114+, Firefox 109+,
 * Safari 17.4+). Falls back to debouncing `scroll` events for older browsers
 * (notably iOS Safari < 17.4), using `fallbackDelayMs` as the settle threshold.
 *
 * The callback is always kept up to date via an internal ref, so it does not
 * need to be memoised by the caller.
 */
export const useScrollEnd = (
  containerRef: RefObject<HTMLElement>,
  callback: () => void,
  fallbackDelayMs = FALLBACK_DELAY_MS,
): void => {
  // Always hold the latest callback without adding it to the effect deps,
  // which would cause the listener to be re-registered on every render.
  const callbackRef = useRef(callback);
  useEffect(() => {
    callbackRef.current = callback;
  });

  useEffect(() => {
    const container = containerRef.current;
    if (!container) return () => {};

    const supportsScrollEnd = 'onscrollend' in HTMLElement.prototype;

    if (supportsScrollEnd) {
      const handler = () => { callbackRef.current(); };
      container.addEventListener('scrollend', handler);
      return () => { container.removeEventListener('scrollend', handler); };
    }

    // Fallback path — debounce scroll events to approximate gesture settle.
    let timer: number | null = null;
    const handler = () => {
      if (timer !== null) window.clearTimeout(timer);
      timer = window.setTimeout(() => {
        timer = null;
        callbackRef.current();
      }, fallbackDelayMs);
    };
    container.addEventListener('scroll', handler, { passive: true });
    return () => {
      container.removeEventListener('scroll', handler);
      if (timer !== null) window.clearTimeout(timer);
    };
  }, [containerRef, fallbackDelayMs]);
};

export default useScrollEnd;
