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

interface UseTapNavigationProps {
  onNext: () => void;
  onPrevious: () => void;
  containerRef: RefObject<HTMLElement>;
  /**
   * Whether tap-zones are divided horizontally or vertically.
   */
  axis?: 'horizontal' | 'vertical';
  disabled?: boolean;
}

/**
 * A hook that enables tap-based navigation on a container element.
 *
 * Horizontal mode: tap left side → previous, tap right side → next.
 * Vertical mode: tap top half → previous, tap bottom half → next.
 * Taps on interactive elements (buttons, links, [role="button"], [data-no-nav]) are always ignored.
 *
 * On touch devices we capture the position at touchstart (before any finger
 * drift that can corrupt the synthetic click coordinates) and confirm the
 * intent on touchend (only if movement is below the tap threshold).
 * Mouse clicks fall through via a normal click listener.
 */
const useTapNavigation = ({
  onNext,
  onPrevious,
  containerRef,
  axis = 'horizontal',
  disabled = false,
}: UseTapNavigationProps) => {
  const onNextRef     = useRef(onNext);
  const onPreviousRef = useRef(onPrevious);

  useEffect(() => {
    onNextRef.current     = onNext;
    onPreviousRef.current = onPrevious;
  }, [onNext, onPrevious]);

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

    // Max finger displacement (px) that still counts as a tap (not a swipe).
    const TAP_THRESHOLD = 10;

    let touchStartX = 0;
    let touchStartY = 0;
    // Store position captured at touchstart so we use the initial press point,
    // not the finger position at release (which may have drifted slightly).
    let touchTapX = 0;
    let touchTapY = 0;
    let isTouchActive = false;

    const navigate = (clientX: number, clientY: number) => {
      // If the tap landed on a button, link, or any explicitly-excluded element,
      // don't navigate — let the element handle its own action.
      const target = document.elementFromPoint(clientX, clientY) as HTMLElement | null;
      if (target?.closest('button, a, [role="button"], [data-no-nav]')) return;

      const rect   = container.getBoundingClientRect();
      const relX   = clientX - rect.left;
      const relY   = clientY - rect.top;
      const width  = rect.width;
      const height = rect.height;

      if (axis === 'vertical') {
        if (relY < height / 2) {
          onPreviousRef.current();
        } else {
          onNextRef.current();
        }
      } else {
        // Left 40% → previous, right 60% → next (asymmetric: next is the more frequent action).
        const leftBoundary = width * 0.4;
        if (relX < leftBoundary) {
          onPreviousRef.current();
        } else {
          onNextRef.current();
        }
      }
    };

    // ---- Touch: capture position at press time, confirm on release ----
    const handleTouchStart = (e: TouchEvent) => {
      if (e.touches.length !== 1) return;
      touchStartX = e.touches[0].clientX;
      touchStartY = e.touches[0].clientY;
      touchTapX   = touchStartX;
      touchTapY   = touchStartY;
      isTouchActive = true;
    };

    const handleTouchEnd = (e: TouchEvent) => {
      if (!isTouchActive) return;
      isTouchActive = false;

      const endX = e.changedTouches[0].clientX;
      const endY = e.changedTouches[0].clientY;
      const moved = Math.max(Math.abs(endX - touchStartX), Math.abs(endY - touchStartY));

      if (moved < TAP_THRESHOLD) {
        // It was a tap — use the touchstart position (most reliable)
        navigate(touchTapX, touchTapY);
      }
    };

    // ---- Mouse: use click (pointer-type guard prevents double-fire on touch) ----
    const handleClick = (e: MouseEvent) => {
      // PointerEvent carries pointerType; skip if already handled via touch path
      if ((e as PointerEvent).pointerType === 'touch') return;
      // Interactive elements handle themselves
      if ((e.target as HTMLElement)?.closest('button, a, [role="button"], [data-no-nav]')) return;
      navigate(e.clientX, e.clientY);
    };

    container.addEventListener('touchstart', handleTouchStart, { passive: true });
    container.addEventListener('touchend',   handleTouchEnd,   { passive: true });
    container.addEventListener('click',      handleClick);

    return () => {
      container.removeEventListener('touchstart', handleTouchStart);
      container.removeEventListener('touchend',   handleTouchEnd);
      container.removeEventListener('click',      handleClick);
    };
  }, [
    containerRef,
    axis,
    disabled,
  ]);
};

export default useTapNavigation;

