import { useRef, useState } from "react";
import { beginPull, finishPull, movePull } from "./pull-to-refresh.js";

const IDLE_PULL = { active: false, distance: 0, armed: false };

export function usePullToRefresh({ direction, onRefresh, mouse = true }) {
  const [pull, setPull] = useState(IDLE_PULL);
  const [refreshing, setRefreshing] = useState(false);
  const pullRef = useRef(IDLE_PULL);
  const refreshingRef = useRef(false);

  function atBoundary(element) {
    if (direction === "down") return element.scrollTop <= 1;
    return element.scrollHeight - element.scrollTop - element.clientHeight <= 1;
  }

  function start(coordinate, element) {
    const next = beginPull({ direction, coordinate, atBoundary: atBoundary(element), refreshing: refreshingRef.current });
    pullRef.current = next;
    setPull(next);
  }

  function move(coordinate, event) {
    const next = movePull(pullRef.current, coordinate);
    pullRef.current = next;
    setPull(next);
    if (next.active && next.distance > 0) event.preventDefault();
  }

  function end(allowRefresh = true) {
    const result = finishPull(pullRef.current);
    pullRef.current = result.state ?? IDLE_PULL;
    setPull(pullRef.current);
    if (allowRefresh && result.shouldRefresh) void runRefresh();
  }

  async function runRefresh() {
    if (refreshingRef.current) return;
    refreshingRef.current = true;
    setRefreshing(true);
    try {
      await onRefresh?.();
    } catch {
      // Feature-owned state exposes request failures where appropriate.
    } finally {
      refreshingRef.current = false;
      setRefreshing(false);
    }
  }

  const pullHandlers = {
    onTouchStart: (event) => start(event.touches[0].clientY, event.currentTarget),
    onTouchMove: (event) => move(event.touches[0].clientY, event),
    onTouchEnd: () => end(true),
    onTouchCancel: () => end(false),
    onPointerDown: (event) => {
      if (!mouse) return;
      if (event.pointerType !== "mouse") return;
      event.currentTarget.setPointerCapture?.(event.pointerId);
      start(event.clientY, event.currentTarget);
    },
    onPointerMove: (event) => {
      if (!mouse) return;
      if (event.pointerType === "mouse" && event.buttons === 1) move(event.clientY, event);
    },
    onPointerUp: (event) => { if (mouse && event.pointerType === "mouse") end(true); },
    onPointerCancel: (event) => { if (mouse && event.pointerType === "mouse") end(false); }
  };

  return { pull: { ...pull, refreshing }, pullHandlers };
}
