import { useCallback, useRef, useState, type TouchEvent } from "react";

interface Options {
  threshold?: number;
  enabled?: boolean;
  maxPull?: number;
}

interface Handlers {
  onTouchStart: (e: TouchEvent<HTMLElement>) => void;
  onTouchMove: (e: TouchEvent<HTMLElement>) => void;
  onTouchEnd: () => void;
  pullDist: number;
  refreshing: boolean;
}

// Hand-rolled pull-to-refresh: records the initial Y on touchstart only if
// the target is scrolled to the top, then tracks downward pull on touchmove.
// On touchend, fires onRefresh if the pull exceeded the threshold.
export function usePullToRefresh(
  onRefresh: () => void | Promise<void>,
  { threshold = 80, enabled = true, maxPull = 120 }: Options = {},
): Handlers {
  const [pullDist, setPullDist] = useState(0);
  const [refreshing, setRefreshing] = useState(false);
  const startYRef = useRef<number | null>(null);

  const onTouchStart = useCallback(
    (e: TouchEvent<HTMLElement>) => {
      if (!enabled || refreshing) return;
      const target = e.currentTarget as HTMLElement;
      if (target.scrollTop > 0) return;
      const t = e.touches[0];
      if (!t) return;
      startYRef.current = t.clientY;
    },
    [enabled, refreshing],
  );

  const onTouchMove = useCallback(
    (e: TouchEvent<HTMLElement>) => {
      if (!enabled || refreshing || startYRef.current === null) return;
      const t = e.touches[0];
      if (!t) return;
      const delta = t.clientY - startYRef.current;
      if (delta > 0) {
        setPullDist(Math.min(delta, maxPull));
      } else {
        setPullDist(0);
      }
    },
    [enabled, refreshing, maxPull],
  );

  const onTouchEnd = useCallback(() => {
    if (!enabled || refreshing) {
      startYRef.current = null;
      setPullDist(0);
      return;
    }
    const fired = pullDist >= threshold && startYRef.current !== null;
    startYRef.current = null;
    setPullDist(0);
    if (fired) {
      setRefreshing(true);
      Promise.resolve(onRefresh())
        .catch(() => {})
        .finally(() => {
          // Hold the indicator briefly so the action feels intentional.
          window.setTimeout(() => setRefreshing(false), 600);
        });
    }
  }, [enabled, refreshing, pullDist, threshold, onRefresh]);

  return { onTouchStart, onTouchMove, onTouchEnd, pullDist, refreshing };
}
