/* swipe-nav.js
   Mobile/desktop swipe & keyboard navigation for entry view.
   - One navigation per gesture
   - Edge protection: horizontal intent + distance + max duration
   - Cooldown prevents multi-advance on long swipes
   - Keyboard: Left/Right arrows (when not typing)
*/
(function () {
  // Bail if not on an entry page with forms
  const prevForm = document.getElementById("navPrevForm");
  const nextForm = document.getElementById("navNextForm");
  if (!prevForm && !nextForm) return;

  // Prefer a dedicated swipe zone if present; fall back to body
  const zone = document.getElementById("swipeZone") || document.body;

  // State
  let tracking = false;
  let startX = 0, startY = 0, startT = 0, pointerId = null;
  let didNavigate = false;
  let cooldown = false;

  // Tunables
  const MIN_X = 48;          // px horizontal distance
  const MAX_ANGLE = 30;      // deg off pure horizontal
  const MAX_MS = 650;        // must complete within this time
  const COOLDOWN_MS = 450;   // after we navigate, ignore further moves briefly

  // Helpers
  function degrees(dx, dy) {
    const a = Math.atan2(Math.abs(dy), Math.abs(dx)) * (180 / Math.PI);
    return a; // 0 = pure horizontal
  }
  function now() { return performance.now ? performance.now() : Date.now(); }

  function reset() {
    tracking = false;
    pointerId = null;
    startX = startY = 0;
    startT = 0;
    didNavigate = false;
  }

  function submit(form) {
    if (!form || cooldown) return;
    cooldown = true;
    form.submit();
    // If navigation is prevented (e.g., disabled button), release cooldown after a tick
    setTimeout(() => { cooldown = false; }, COOLDOWN_MS);
  }

  // Pointer events if supported (best across devices). Fallback to touch.
  const usePointer = "PointerEvent" in window;

  function onDown(e) {
    if (cooldown) return;
    // Ignore right click / secondary
    if (usePointer && e.pointerType === "mouse" && e.button !== 0) return;

    tracking = true;
    didNavigate = false;

    const pt = usePointer ? e : (e.touches && e.touches[0]);
    if (!pt) return;

    pointerId = usePointer ? e.pointerId : pt.identifier;
    startX = pt.clientX;
    startY = pt.clientY;
    startT = now();

    // Once we suspect horizontal motion, we'll call preventDefault in move
  }

  function onMove(e) {
    if (!tracking || didNavigate || cooldown) return;

    const pt = usePointer ? e : (e.touches && e.touches[0]);
    if (!pt) return;
    if (usePointer && e.pointerId !== pointerId) return;

    const dx = pt.clientX - startX;
    const dy = pt.clientY - startY;

    // If user is panning mostly horizontally, stop the browser from doing a horizontal scroll/jiggle
    if (Math.abs(dx) > 8 && Math.abs(dx) > Math.abs(dy)) {
      // In touch events you must preventDefault during move to suppress scroll
      if (e.cancelable) e.preventDefault();
    }
  }

  function onUp(e) {
    if (!tracking || didNavigate || cooldown) { reset(); return; }

    let pt;
    if (usePointer) {
      if (e.pointerId !== pointerId) return reset();
      pt = e;
    } else {
      // On touchend, touches[] is empty; use changedTouches[]
      pt = (e.changedTouches && e.changedTouches[0]) || null;
      if (!pt) return reset();
    }

    const dx = pt.clientX - startX;
    const dy = pt.clientY - startY;
    const dt = now() - startT;

    const ang = degrees(dx, dy);
    const horizontalEnough = ang <= MAX_ANGLE;

    // Must be a deliberate, reasonably quick horizontal swipe
    if (Math.abs(dx) >= MIN_X && horizontalEnough && dt <= MAX_MS) {
      didNavigate = true;
      if (dx < 0) {
        // left swipe -> NEXT
        const btn = nextForm && nextForm.querySelector("button[disabled]");
        if (!btn) submit(nextForm);
      } else {
        // right swipe -> PREV
        const btn = prevForm && prevForm.querySelector("button[disabled]");
        if (!btn) submit(prevForm);
      }
      if (e.cancelable) e.preventDefault();
    }

    reset();
  }

  function onCancel() { reset(); }

  if (usePointer) {
    zone.addEventListener("pointerdown", onDown, { passive: true });
    zone.addEventListener("pointermove", onMove, { passive: false });
    zone.addEventListener("pointerup", onUp, { passive: false });
    zone.addEventListener("pointercancel", onCancel, { passive: true });
    zone.addEventListener("pointerleave", onCancel, { passive: true });
  } else {
    zone.addEventListener("touchstart", onDown, { passive: true });
    zone.addEventListener("touchmove", onMove, { passive: false });
    zone.addEventListener("touchend", onUp, { passive: false });
    zone.addEventListener("touchcancel", onCancel, { passive: true });
  }

  // Keyboard support (left/right arrows) – ignore if typing
  document.addEventListener("keydown", (e) => {
    if (cooldown) return;
    const tag = (e.target && e.target.tagName) || "";
    const typing = /INPUT|TEXTAREA|SELECT/.test(tag);
    if (typing) return;

    if (e.key === "ArrowRight") {
      const btn = nextForm && nextForm.querySelector("button[disabled]");
      if (!btn) { e.preventDefault(); submit(nextForm); }
    } else if (e.key === "ArrowLeft") {
      const btn = prevForm && prevForm.querySelector("button[disabled]");
      if (!btn) { e.preventDefault(); submit(prevForm); }
    }
  });

  // Defensive CSS: lock horizontal gestures to our handler only inside the zone
  (function injectCSS() {
    const css = `
      #swipeZone{ touch-action: pan-y; overscroll-behavior-x: none; overflow-x: hidden; }
      html, body{ overscroll-behavior-x: none; }
    `;
    const el = document.createElement("style");
    el.textContent = css;
    document.head.appendChild(el);
  })();
})();