/**
 * `useSwipeDismiss` — pointer + touch drag state machine for swipe-to-
 * dismiss gestures. Full port of Base UI React's
 * `packages/react/src/utils/useSwipeDismiss.ts` (~1000 LOC).
 *
 * Solid adaptations vs. the React source:
 *
 *  - `elementRef: RefObject<HTMLElement | null>` → `elementRef: () =>
 *    HTMLElement | null` (accessor). Callers pass a signal / accessor
 *    produced by the owning component.
 *  - `enabled: boolean` → `enabled: () => boolean` (accessor) per
 *    solidify.md P3 — lets consumers flip the hook on/off reactively
 *    without remounting.
 *  - `useState` for `isSwiping` / `dragOffset` / `initialTransform` /
 *    `lockedDirection` / `currentSwipeDirection` / `dragDismissed` →
 *    `createSignal` with `ownedWrite: true` (owned, written from event
 *    handlers).
 *  - `useStableCallback` → plain closures — Solid doesn't re-render and
 *    event handlers close over mutable state directly.
 *  - React's `React.PointerEvent` / `React.TouchEvent` are plain DOM
 *    events here. `event.nativeEvent` is rewritten to `event` itself;
 *    callers of `onDismiss` / `onRelease` / `onSwipeStart` receive the
 *    native DOM event directly.
 *  - Return shape matches React (`swiping` / `swipeDirection` / ... /
 *    `getPointerProps` / `getTouchProps` / `getDragStyles` / `reset`)
 *    except `swiping` / `swipeDirection` / `dragDismissed` are accessors
 *    (functions), not plain values.
 *
 * No comments annotate the React → Solid deltas at each call site; the
 * behavior is 1:1.
 */

import { createSignal } from "solid-js";
import { contains } from "./dom/contains.jsx";
import { getTarget } from "./dom/get-target.jsx";
import { ownerDocument } from "./dom/owner-document.jsx";
import { ownerWindow } from "./dom/owner-window.jsx";
import { getElementAtPoint } from "./get-element-at-point.jsx";
import { findScrollableTouchTarget, hasScrollableAncestor } from "./scrollable.jsx";
import { clamp } from "./slider-utils/clamp.jsx";
const DEFAULT_SWIPE_THRESHOLD = 40;
const REVERSE_CANCEL_THRESHOLD = 10;
const MIN_DRAG_THRESHOLD = 1;
const MIN_VELOCITY_DURATION_MS = 50;
const MIN_RELEASE_VELOCITY_DURATION_MS = 16;
const MAX_RELEASE_VELOCITY_AGE_MS = 80;
const DEFAULT_IGNORE_SELECTOR = 'button,a,input,select,textarea,label,[role="button"]';

/**
 * Returns the signed displacement of `deltaX` / `deltaY` along the given
 * direction. A positive return value means motion toward the dismiss edge.
 */
export function getDisplacement(direction, deltaX, deltaY) {
  switch (direction) {
    case "up":
      return -deltaY;
    case "down":
      return deltaY;
    case "left":
      return -deltaX;
    case "right":
      return deltaX;
    default:
      return 0;
  }
}

/**
 * Parses `transform` off the computed style, returning translation +
 * uniform-scale components. Mirrors React's implementation.
 */
export function getElementTransform(element) {
  const computedStyle = ownerWindow(element).getComputedStyle(element);
  const transform = computedStyle.transform;
  let translateX = 0;
  let translateY = 0;
  let scale = 1;
  if (transform && transform !== "none") {
    const matrix = transform.match(/matrix(?:3d)?\(([^)]+)\)/);
    if (matrix?.[1]) {
      const values = matrix[1].split(", ").map(Number.parseFloat);
      if (values.length === 6) {
        translateX = values[4] ?? 0;
        translateY = values[5] ?? 0;
        const a = values[0] ?? 0;
        const b = values[1] ?? 0;
        scale = Math.sqrt(a * a + b * b);
      } else if (values.length === 16) {
        translateX = values[12] ?? 0;
        translateY = values[13] ?? 0;
        scale = values[0] ?? 1;
      }
    }
  }
  return {
    x: translateX,
    y: translateY,
    scale
  };
}
function getValidTimeStamp(timeStamp) {
  return Number.isFinite(timeStamp) && timeStamp > 0 ? timeStamp : null;
}
function hasPrimaryMouseButton(buttons) {
  return buttons % 2 === 1;
}
function safelyChangePointerCapture(element, pointerId, method) {
  const fn = element[method];
  if (typeof fn !== "function") return;
  try {
    fn.call(element, pointerId);
  } catch (error) {
    if (error && typeof error === "object" && "name" in error && error.name === "NotFoundError") {
      return;
    }
    throw error;
  }
}
export function useSwipeDismiss(options) {
  const {
    directions,
    elementRef,
    movementCssVars,
    trackDrag: trackDragOption
  } = options;
  const trackDrag = trackDragOption ?? true;
  const ignoreSelectorWhenTouch = options.ignoreSelectorWhenTouch ?? true;
  const ignoreScrollableAncestors = options.ignoreScrollableAncestors ?? false;
  const swipeThresholdProp = options.swipeThreshold;
  const ignoreSelector = DEFAULT_IGNORE_SELECTOR;
  const primaryDirection = directions.length === 1 ? directions[0] : undefined;
  const swipeThresholdDefault = Math.max(0, typeof swipeThresholdProp === "number" ? swipeThresholdProp : DEFAULT_SWIPE_THRESHOLD);
  const allowLeft = directions.includes("left");
  const allowRight = directions.includes("right");
  const allowUp = directions.includes("up");
  const allowDown = directions.includes("down");
  const hasHorizontal = allowLeft || allowRight;
  const hasVertical = allowUp || allowDown;
  const scrollAxes = [];
  if (hasVertical) scrollAxes.push("vertical");
  if (hasHorizontal) scrollAxes.push("horizontal");
  const [currentSwipeDirection, setCurrentSwipeDirection] = createSignal(undefined, {
    ownedWrite: true
  });
  const [isSwiping, setIsSwipingSig] = createSignal(false, {
    ownedWrite: true
  });
  const [isRealSwipe, setIsRealSwipe] = createSignal(false, {
    ownedWrite: true
  });
  const [dragDismissed, setDragDismissed] = createSignal(false, {
    ownedWrite: true
  });
  const [dragOffsetX, setDragOffsetX] = createSignal(0, {
    ownedWrite: true
  });
  const [dragOffsetY, setDragOffsetY] = createSignal(0, {
    ownedWrite: true
  });
  const [initialTransformX, setInitialTransformX] = createSignal(0, {
    ownedWrite: true
  });
  const [initialTransformY, setInitialTransformY] = createSignal(0, {
    ownedWrite: true
  });
  const [initialTransformScale, setInitialTransformScale] = createSignal(1, {
    ownedWrite: true
  });
  const [lockedDirection, setLockedDirection] = createSignal(null, {
    ownedWrite: true
  });

  // Mutable refs (not reactive — mirror React's `useRef`).
  let dragStartPos = {
    x: 0,
    y: 0
  };
  let dragOffsetRef = {
    x: 0,
    y: 0
  };
  let lastMovePos = null;
  let initialTransformRef = {
    x: 0,
    y: 0,
    scale: 1
  };
  let intendedSwipeDirection;
  let maxSwipeDisplacement = 0;
  let cancelledSwipe = false;
  let swipeCancelBaseline = {
    x: 0,
    y: 0
  };
  let isFirstPointerMove = false;
  let pendingSwipe = false;
  let pendingSwipeStartPos = null;
  let swipeFromScrollable = false;
  let sawPrimaryButtonsOnMove = false;
  let elementSize = {
    width: 0,
    height: 0
  };
  let swipeProgressValue = 0;
  let swipeThresholdRef = swipeThresholdDefault;
  let swipeStartTime = null;
  let lastDragSample = null;
  let lastDragVelocity = {
    x: 0,
    y: 0
  };
  let lastProgressDetails = null;
  let isSwipingRef = false;
  const setSwiping = nextSwiping => {
    if (isSwipingRef === nextSwiping) return;
    isSwipingRef = nextSwiping;
    setIsSwipingSig(nextSwiping);
    options.onSwipingChange?.(nextSwiping);
  };
  function resolveSwipeThreshold(direction) {
    if (!direction) return;
    if (typeof swipeThresholdProp !== "function") {
      swipeThresholdRef = swipeThresholdDefault;
      return;
    }
    const element = elementRef();
    if (!element) return;
    const value = swipeThresholdProp({
      element,
      direction
    });
    swipeThresholdRef = Math.max(0, value);
  }
  const updateSwipeProgress = (progress, details) => {
    const nextProgress = Number.isFinite(progress) ? clamp(progress, 0, 1) : 0;
    const progressChanged = nextProgress !== swipeProgressValue;
    let detailsChanged = false;
    if (details) {
      const last = lastProgressDetails;
      detailsChanged = !last || last.deltaX !== details.deltaX || last.deltaY !== details.deltaY || last.direction !== details.direction;
    }
    if (!progressChanged && !detailsChanged) return;
    swipeProgressValue = nextProgress;
    if (details) {
      lastProgressDetails = details;
    } else if (progressChanged) {
      lastProgressDetails = null;
    }
    options.onProgress?.(nextProgress, details);
  };
  function recordDragSample(offset, timeStamp) {
    if (timeStamp === null) return;
    const last = lastDragSample;
    if (last && timeStamp > last.time) {
      const durationMs = Math.max(timeStamp - last.time, MIN_RELEASE_VELOCITY_DURATION_MS);
      lastDragVelocity = {
        x: (offset.x - last.x) / durationMs,
        y: (offset.y - last.y) / durationMs
      };
    }
    lastDragSample = {
      x: offset.x,
      y: offset.y,
      time: timeStamp
    };
  }
  const reset = () => {
    setCurrentSwipeDirection(undefined);
    setSwiping(false);
    setIsRealSwipe(false);
    setDragDismissed(false);
    setDragOffsetX(0);
    setDragOffsetY(0);
    setInitialTransformX(0);
    setInitialTransformY(0);
    setInitialTransformScale(1);
    setLockedDirection(null);
    updateSwipeProgress(0);
    swipeThresholdRef = swipeThresholdDefault;
    dragStartPos = {
      x: 0,
      y: 0
    };
    dragOffsetRef = {
      x: 0,
      y: 0
    };
    initialTransformRef = {
      x: 0,
      y: 0,
      scale: 1
    };
    intendedSwipeDirection = undefined;
    maxSwipeDisplacement = 0;
    cancelledSwipe = false;
    swipeCancelBaseline = {
      x: 0,
      y: 0
    };
    isFirstPointerMove = false;
    lastMovePos = null;
    pendingSwipe = false;
    pendingSwipeStartPos = null;
    swipeFromScrollable = false;
    sawPrimaryButtonsOnMove = false;
    elementSize = {
      width: 0,
      height: 0
    };
    swipeStartTime = null;
    lastDragSample = null;
    lastDragVelocity = {
      x: 0,
      y: 0
    };
    lastProgressDetails = null;
  };
  function getPrimaryPointerPosition(event) {
    if ("touches" in event) {
      const touch = event.touches[0];
      return touch ? {
        x: touch.clientX,
        y: touch.clientY
      } : null;
    }
    return {
      x: event.clientX,
      y: event.clientY
    };
  }
  function isTouchLikeEvent(event) {
    if ("touches" in event) return true;
    return event.pointerType === "touch";
  }
  function getTargetAtPoint(position, nativeEvent) {
    const doc = ownerDocument(elementRef());
    const elementAtPoint = getElementAtPoint(doc, position.x, position.y);
    const target = elementAtPoint ?? getTarget(nativeEvent);
    return target;
  }
  function findGestureScrollableTouchTarget(target, root) {
    if (hasHorizontal && !hasVertical) {
      return findScrollableTouchTarget(target, root, "horizontal");
    }
    if (hasVertical && !hasHorizontal) {
      return findScrollableTouchTarget(target, root, "vertical");
    }
    return findScrollableTouchTarget(target, root, "vertical") ?? findScrollableTouchTarget(target, root, "horizontal");
  }
  function startSwipeAtPosition(event, position, startOptions) {
    swipeFromScrollable = false;
    const touchLike = isTouchLikeEvent(event);
    const target = getTargetAtPoint(position, event);
    const doc = ownerDocument(elementRef());
    const body = doc.body;
    const scrollableTarget = touchLike && body ? findGestureScrollableTouchTarget(target, body) : null;
    const ignoreScrollableTarget = startOptions?.ignoreScrollableTarget ?? false;
    if (scrollableTarget && !ignoreScrollableTarget) return false;
    swipeFromScrollable = Boolean(scrollableTarget && ignoreScrollableTarget);
    const isInteractiveElement = target ? target.closest(ignoreSelector) : false;
    if (isInteractiveElement && (!touchLike || ignoreSelectorWhenTouch)) {
      return false;
    }
    const element = elementRef();
    if (ignoreScrollableAncestors && element && target && scrollAxes.length > 0) {
      const ignoreAncestors = startOptions?.ignoreScrollableAncestors ?? false;
      if (!ignoreAncestors && hasScrollableAncestor(target, element, scrollAxes)) {
        return false;
      }
    }
    cancelledSwipe = false;
    intendedSwipeDirection = undefined;
    maxSwipeDisplacement = 0;
    dragStartPos = position;
    swipeStartTime = getValidTimeStamp(event.timeStamp);
    swipeCancelBaseline = position;
    lastMovePos = position;
    if (element) {
      elementSize = {
        width: element.offsetWidth,
        height: element.offsetHeight
      };
      resolveSwipeThreshold(primaryDirection);
      const transform = getElementTransform(element);
      initialTransformRef = transform;
      dragOffsetRef = {
        x: transform.x,
        y: transform.y
      };
      setInitialTransformX(transform.x);
      setInitialTransformY(transform.y);
      setInitialTransformScale(transform.scale);
      setDragOffsetX(transform.x);
      setDragOffsetY(transform.y);
      recordDragSample({
        x: transform.x,
        y: transform.y
      }, swipeStartTime);
      if (!("touches" in event)) {
        safelyChangePointerCapture(element, event.pointerId, "setPointerCapture");
      }
    }
    options.onSwipeStart?.(event);
    setSwiping(true);
    setIsRealSwipe(false);
    setLockedDirection(null);
    isFirstPointerMove = true;
    updateSwipeProgress(0);
    return true;
  }
  function resetPendingSwipeState() {
    clearPendingSwipeStartState();
    swipeFromScrollable = false;
    lastMovePos = null;
  }
  function clearPendingSwipeStartState() {
    pendingSwipe = false;
    pendingSwipeStartPos = null;
  }
  function cancelSwipeInteraction(event) {
    resetPendingSwipeState();
    if (!isSwipingRef) return;
    setSwiping(false);
    setIsRealSwipe(false);
    setLockedDirection(null);
    const resolvedInitialTransform = trackDrag ? {
      x: initialTransformX(),
      y: initialTransformY()
    } : {
      x: initialTransformRef.x,
      y: initialTransformRef.y
    };
    dragOffsetRef = {
      x: resolvedInitialTransform.x,
      y: resolvedInitialTransform.y
    };
    setDragOffsetX(resolvedInitialTransform.x);
    setDragOffsetY(resolvedInitialTransform.y);
    setCurrentSwipeDirection(undefined);
    sawPrimaryButtonsOnMove = false;
    const element = elementRef();
    if (element) {
      safelyChangePointerCapture(element, event.pointerId, "releasePointerCapture");
    }
    updateSwipeProgress(0, {
      deltaX: 0,
      deltaY: 0,
      direction: undefined
    });
    options.onCancel?.(event);
  }
  function applyDirectionalDamping(deltaX, deltaY) {
    const exponent = value => value >= 0 ? value ** 0.5 : -(Math.abs(value) ** 0.5);
    const dampAxis = (delta, allowNegative, allowPositive) => {
      if (!allowNegative && delta < 0) return exponent(delta);
      if (!allowPositive && delta > 0) return exponent(delta);
      return delta;
    };
    const newDeltaX = hasHorizontal ? dampAxis(deltaX, allowLeft, allowRight) : exponent(deltaX);
    const newDeltaY = hasVertical ? dampAxis(deltaY, allowUp, allowDown) : exponent(deltaY);
    return {
      x: newDeltaX,
      y: newDeltaY
    };
  }
  function canSwipeFromScrollEdgeOnPendingMove(scrollTarget, deltaX, deltaY) {
    const absDeltaX = Math.abs(deltaX);
    const absDeltaY = Math.abs(deltaY);
    const useVerticalAxis = hasVertical && deltaY !== 0 && (!hasHorizontal || absDeltaY >= absDeltaX);
    if (useVerticalAxis) {
      const maxScrollTop = Math.max(0, scrollTarget.scrollHeight - scrollTarget.clientHeight);
      const atTop = scrollTarget.scrollTop <= 0;
      const atBottom = scrollTarget.scrollTop >= maxScrollTop;
      const movingDown = deltaY > 0;
      const movingUp = deltaY < 0;
      const canSwipeDown = movingDown && atTop && allowDown;
      const canSwipeUp = movingUp && atBottom && allowUp;
      return canSwipeDown || canSwipeUp;
    }
    const useHorizontalAxis = hasHorizontal && deltaX !== 0 && (!hasVertical || absDeltaX > absDeltaY);
    if (useHorizontalAxis) {
      const maxScrollLeft = Math.max(0, scrollTarget.scrollWidth - scrollTarget.clientWidth);
      const atLeft = scrollTarget.scrollLeft <= 0;
      const atRight = scrollTarget.scrollLeft >= maxScrollLeft;
      const movingRight = deltaX > 0;
      const movingLeft = deltaX < 0;
      const canSwipeRight = movingRight && atLeft && allowRight;
      const canSwipeLeft = movingLeft && atRight && allowLeft;
      return canSwipeRight || canSwipeLeft;
    }
    return null;
  }
  const handleStart = event => {
    if (!options.enabled()) return;
    if (event.defaultPrevented) return;
    if (!("touches" in event) && event.button !== 0) return;
    const startPos = getPrimaryPointerPosition(event);
    if (!startPos) return;
    pendingSwipe = true;
    pendingSwipeStartPos = startPos;
    swipeFromScrollable = false;
    sawPrimaryButtonsOnMove = false;
    const allowedToStart = options.canStart ? options.canStart(startPos, {
      nativeEvent: event,
      direction: primaryDirection
    }) : true;
    if (!allowedToStart) return;
    if (startSwipeAtPosition(event, startPos)) {
      clearPendingSwipeStartState();
    }
  };
  function handleMoveCore(event, position, movement) {
    if (!options.enabled() || !isSwipingRef) return;
    const target = getTarget(event);
    if (isTouchLikeEvent(event) && !swipeFromScrollable) {
      const boundaryElement = event.currentTarget;
      if (findGestureScrollableTouchTarget(target, boundaryElement)) return;
    }
    if (!("touches" in event)) {
      // Prevent text selection on Safari during a pointer drag.
      event.preventDefault();
    }
    if (isFirstPointerMove) {
      dragStartPos = position;
      const moveTime = getValidTimeStamp(event.timeStamp);
      if (moveTime !== null) swipeStartTime = moveTime;
      isFirstPointerMove = false;
    }
    const clientX = position.x;
    const clientY = position.y;
    const movementX = movement.x;
    const movementY = movement.y;
    if (movementY < 0 && clientY > swipeCancelBaseline.y || movementY > 0 && clientY < swipeCancelBaseline.y) {
      swipeCancelBaseline = {
        x: swipeCancelBaseline.x,
        y: clientY
      };
    }
    if (movementX < 0 && clientX > swipeCancelBaseline.x || movementX > 0 && clientX < swipeCancelBaseline.x) {
      swipeCancelBaseline = {
        x: clientX,
        y: swipeCancelBaseline.y
      };
    }
    const deltaX = clientX - dragStartPos.x;
    const deltaY = clientY - dragStartPos.y;
    const cancelDeltaY = clientY - swipeCancelBaseline.y;
    const cancelDeltaX = clientX - swipeCancelBaseline.x;
    if (!isRealSwipe()) {
      const movementDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
      if (movementDistance >= MIN_DRAG_THRESHOLD) {
        setIsRealSwipe(true);
        if (lockedDirection() === null) {
          if (hasHorizontal && hasVertical) {
            const absX = Math.abs(deltaX);
            const absY = Math.abs(deltaY);
            setLockedDirection(absX > absY ? "horizontal" : "vertical");
          }
        }
      }
    }
    let candidate;
    if (!intendedSwipeDirection) {
      const locked = lockedDirection();
      if (locked === "vertical") {
        if (deltaY > 0) candidate = "down";else if (deltaY < 0) candidate = "up";
      } else if (locked === "horizontal") {
        if (deltaX > 0) candidate = "right";else if (deltaX < 0) candidate = "left";
      } else if (Math.abs(deltaX) >= Math.abs(deltaY)) {
        candidate = deltaX > 0 ? "right" : "left";
      } else {
        candidate = deltaY > 0 ? "down" : "up";
      }
      if (candidate) {
        const isAllowed = candidate === "left" && allowLeft || candidate === "right" && allowRight || candidate === "up" && allowUp || candidate === "down" && allowDown;
        if (isAllowed) {
          intendedSwipeDirection = candidate;
          maxSwipeDisplacement = getDisplacement(candidate, deltaX, deltaY);
          setCurrentSwipeDirection(candidate);
          resolveSwipeThreshold(candidate);
        }
      }
    } else {
      const direction = intendedSwipeDirection;
      const currentDisplacement = getDisplacement(direction, cancelDeltaX, cancelDeltaY);
      if (currentDisplacement > swipeThresholdRef) {
        cancelledSwipe = false;
        setCurrentSwipeDirection(direction);
      } else if (!(allowLeft && allowRight) && !(allowUp && allowDown) && maxSwipeDisplacement - currentDisplacement >= REVERSE_CANCEL_THRESHOLD) {
        cancelledSwipe = true;
      }
    }
    const dampedDelta = applyDirectionalDamping(deltaX, deltaY);
    let newOffsetX = initialTransformRef.x;
    let newOffsetY = initialTransformRef.y;
    const locked = lockedDirection();
    if (locked === "horizontal") {
      if (hasHorizontal) newOffsetX += dampedDelta.x;
    } else if (locked === "vertical") {
      if (hasVertical) newOffsetY += dampedDelta.y;
    } else {
      if (hasHorizontal) newOffsetX += dampedDelta.x;
      if (hasVertical) newOffsetY += dampedDelta.y;
    }
    dragOffsetRef = {
      x: newOffsetX,
      y: newOffsetY
    };
    if (trackDrag) {
      setDragOffsetX(newOffsetX);
      setDragOffsetY(newOffsetY);
    }
    recordDragSample({
      x: newOffsetX,
      y: newOffsetY
    }, getValidTimeStamp(event.timeStamp));
    const dragDeltaX = newOffsetX - initialTransformRef.x;
    const dragDeltaY = newOffsetY - initialTransformRef.y;
    const swipeDirectionDetails = intendedSwipeDirection;
    const progressDirection = primaryDirection ?? intendedSwipeDirection;
    if (!progressDirection) {
      updateSwipeProgress(0, {
        deltaX: dragDeltaX,
        deltaY: dragDeltaY,
        direction: swipeDirectionDetails
      });
      return;
    }
    const size = progressDirection === "left" || progressDirection === "right" ? elementSize.width : elementSize.height;
    const scale = initialTransformRef.scale || 1;
    if (size <= 0 || scale <= 0) {
      updateSwipeProgress(0, {
        deltaX: dragDeltaX,
        deltaY: dragDeltaY,
        direction: swipeDirectionDetails
      });
      return;
    }
    const progressDisplacement = getDisplacement(progressDirection, newOffsetX - initialTransformRef.x, newOffsetY - initialTransformRef.y);
    if (progressDisplacement <= 0) {
      updateSwipeProgress(0, {
        deltaX: dragDeltaX,
        deltaY: dragDeltaY,
        direction: swipeDirectionDetails
      });
      return;
    }
    updateSwipeProgress(progressDisplacement / (size * scale), {
      deltaX: dragDeltaX,
      deltaY: dragDeltaY,
      direction: swipeDirectionDetails
    });
  }
  const handleMove = event => {
    const currentPos = getPrimaryPointerPosition(event);
    if (!currentPos) return;
    if (!("touches" in event)) {
      const hasPrimaryButton = hasPrimaryMouseButton(event.buttons);
      if (hasPrimaryButton) sawPrimaryButtonsOnMove = true;
      const lostPrimaryButtonDuringSwipe = event.buttons === 0 && sawPrimaryButtonsOnMove;
      if (event.buttons !== 0 && !hasPrimaryButton || lostPrimaryButtonDuringSwipe) {
        cancelSwipeInteraction(event);
        return;
      }
    }
    if (!isSwiping() && pendingSwipe) {
      if (!isTouchLikeEvent(event) && event.defaultPrevented) {
        resetPendingSwipeState();
        return;
      }
      const allowedToStart = options.canStart ? options.canStart(currentPos, {
        nativeEvent: event,
        direction: primaryDirection
      }) : true;
      if (allowedToStart) {
        const pendingStartPos = pendingSwipeStartPos;
        let ignoreScrollableOnStart = false;
        if (isTouchLikeEvent(event)) {
          const element = elementRef();
          if (pendingStartPos && element) {
            const target = getTargetAtPoint(currentPos, event);
            const doc = ownerDocument(element);
            const body = doc.body;
            const scrollTarget = body ? findGestureScrollableTouchTarget(target, body) : null;
            if (scrollTarget && (contains(element, scrollTarget) || contains(scrollTarget, element))) {
              const deltaX = currentPos.x - pendingStartPos.x;
              const deltaY = currentPos.y - pendingStartPos.y;
              const canSwipeFromEdge = canSwipeFromScrollEdgeOnPendingMove(scrollTarget, deltaX, deltaY);
              if (canSwipeFromEdge === false) return;
              if (canSwipeFromEdge === true) ignoreScrollableOnStart = true;
            }
          }
        }
        const started = startSwipeAtPosition(event, currentPos, {
          ignoreScrollableTarget: ignoreScrollableOnStart,
          ignoreScrollableAncestors: ignoreScrollableOnStart
        });
        if (started) {
          if (pendingStartPos && ignoreScrollableOnStart) {
            clearPendingSwipeStartState();
            dragStartPos = pendingStartPos;
            swipeCancelBaseline = pendingStartPos;
            lastMovePos = pendingStartPos;
            isFirstPointerMove = false;
          } else {
            clearPendingSwipeStartState();
            swipeFromScrollable = false;
          }
        }
      }
    }
    const previousPos = lastMovePos;
    const movement = previousPos === null ? {
      x: 0,
      y: 0
    } : {
      x: currentPos.x - previousPos.x,
      y: currentPos.y - previousPos.y
    };
    lastMovePos = currentPos;
    handleMoveCore(event, currentPos, movement);
  };
  const handleEnd = event => {
    if (!options.enabled()) return;
    const resolvedDragOffset = dragOffsetRef;
    const resolvedInitialTransform = initialTransformRef;
    const releaseDeltaX = resolvedDragOffset.x - resolvedInitialTransform.x;
    const releaseDeltaY = resolvedDragOffset.y - resolvedInitialTransform.y;
    const progressDetails = {
      deltaX: releaseDeltaX,
      deltaY: releaseDeltaY,
      direction: currentSwipeDirection() ?? intendedSwipeDirection
    };
    if (!isSwipingRef) {
      resetPendingSwipeState();
      updateSwipeProgress(0, progressDetails);
      return;
    }
    setSwiping(false);
    setIsRealSwipe(false);
    setLockedDirection(null);
    resetPendingSwipeState();
    sawPrimaryButtonsOnMove = false;
    const element = elementRef();
    if (element) {
      if (!("touches" in event)) {
        safelyChangePointerCapture(element, event.pointerId, "releasePointerCapture");
      }
    }
    const deltaX = releaseDeltaX;
    const deltaY = releaseDeltaY;
    const startTime = swipeStartTime;
    const endTime = getValidTimeStamp(event.timeStamp);
    const durationMs = startTime !== null && endTime !== null && endTime > startTime ? endTime - startTime : 0;
    const velocityDurationMs = durationMs > 0 ? Math.max(durationMs, MIN_VELOCITY_DURATION_MS) : 0;
    const velocityX = velocityDurationMs > 0 ? deltaX / velocityDurationMs : 0;
    const velocityY = velocityDurationMs > 0 ? deltaY / velocityDurationMs : 0;
    let releaseVelocityX = lastDragVelocity.x;
    let releaseVelocityY = lastDragVelocity.y;
    const lastSample = lastDragSample;
    if (lastSample && endTime !== null && endTime >= lastSample.time) {
      const ageMs = endTime - lastSample.time;
      if (ageMs <= MAX_RELEASE_VELOCITY_AGE_MS) {
        const sampleDurationMs = Math.max(ageMs, MIN_RELEASE_VELOCITY_DURATION_MS);
        const deltaFromLastSampleX = resolvedDragOffset.x - lastSample.x;
        const deltaFromLastSampleY = resolvedDragOffset.y - lastSample.y;
        const sampleVelocityX = deltaFromLastSampleX / sampleDurationMs;
        const sampleVelocityY = deltaFromLastSampleY / sampleDurationMs;
        if (sampleVelocityX !== 0) releaseVelocityX = sampleVelocityX;
        if (sampleVelocityY !== 0) releaseVelocityY = sampleVelocityY;
      } else {
        releaseVelocityX = 0;
        releaseVelocityY = 0;
      }
    }
    const releaseDecision = options.onRelease?.({
      event: event,
      direction: currentSwipeDirection() ?? intendedSwipeDirection,
      deltaX,
      deltaY,
      velocityX,
      velocityY,
      releaseVelocityX,
      releaseVelocityY
    });
    const hasReleaseDecision = typeof releaseDecision === "boolean";
    if (cancelledSwipe && !hasReleaseDecision) {
      dragOffsetRef = {
        x: resolvedInitialTransform.x,
        y: resolvedInitialTransform.y
      };
      setDragOffsetX(resolvedInitialTransform.x);
      setDragOffsetY(resolvedInitialTransform.y);
      setCurrentSwipeDirection(undefined);
      updateSwipeProgress(0, progressDetails);
      return;
    }
    let shouldClose = false;
    let dismissDirection;
    if (hasReleaseDecision) {
      shouldClose = releaseDecision;
      dismissDirection = currentSwipeDirection() ?? intendedSwipeDirection ?? primaryDirection;
    } else {
      for (const direction of directions) {
        switch (direction) {
          case "right":
            if (deltaX > swipeThresholdRef) {
              shouldClose = true;
              dismissDirection = "right";
            }
            break;
          case "left":
            if (deltaX < -swipeThresholdRef) {
              shouldClose = true;
              dismissDirection = "left";
            }
            break;
          case "down":
            if (deltaY > swipeThresholdRef) {
              shouldClose = true;
              dismissDirection = "down";
            }
            break;
          case "up":
            if (deltaY < -swipeThresholdRef) {
              shouldClose = true;
              dismissDirection = "up";
            }
            break;
          default:
            break;
        }
        if (shouldClose) break;
      }
    }
    if (shouldClose && dismissDirection) {
      setCurrentSwipeDirection(dismissDirection);
      setDragDismissed(true);
      options.onDismiss?.(event, {
        direction: dismissDirection
      });
    } else {
      dragOffsetRef = {
        x: resolvedInitialTransform.x,
        y: resolvedInitialTransform.y
      };
      setDragOffsetX(resolvedInitialTransform.x);
      setDragOffsetY(resolvedInitialTransform.y);
      setCurrentSwipeDirection(undefined);
      updateSwipeProgress(0, progressDetails);
    }
  };
  const getDragStyles = () => {
    const resolvedDragOffset = trackDrag ? {
      x: dragOffsetX(),
      y: dragOffsetY()
    } : {
      x: dragOffsetRef.x,
      y: dragOffsetRef.y
    };
    const resolvedInitialTransform = trackDrag ? {
      x: initialTransformX(),
      y: initialTransformY(),
      scale: initialTransformScale()
    } : initialTransformRef;
    const swiping = isSwiping();
    if (!swiping && resolvedDragOffset.x === resolvedInitialTransform.x && resolvedDragOffset.y === resolvedInitialTransform.y && !dragDismissed()) {
      return {
        [movementCssVars.x]: "0px",
        [movementCssVars.y]: "0px"
      };
    }
    const deltaX = resolvedDragOffset.x - resolvedInitialTransform.x;
    const deltaY = resolvedDragOffset.y - resolvedInitialTransform.y;
    return {
      transition: swiping ? "none" : undefined,
      transform: swiping ? `translateX(${resolvedDragOffset.x}px) translateY(${resolvedDragOffset.y}px) scale(${resolvedInitialTransform.scale})` : undefined,
      [movementCssVars.x]: `${deltaX}px`,
      [movementCssVars.y]: `${deltaY}px`
    };
  };
  const getPointerProps = () => {
    if (!options.enabled()) return {};
    return {
      onPointerDown: handleStart,
      onPointerMove: handleMove,
      onPointerUp: handleEnd,
      onPointerCancel: handleEnd
    };
  };
  const getTouchProps = () => {
    if (!options.enabled()) return {};
    return {
      onTouchStart: handleStart,
      onTouchMove: handleMove,
      onTouchEnd: handleEnd,
      onTouchCancel: handleEnd
    };
  };
  return {
    swiping: isSwiping,
    swipeDirection: currentSwipeDirection,
    dragDismissed,
    getPointerProps,
    getTouchProps,
    getDragStyles,
    reset
  };
}
//# sourceMappingURL=use-swipe-dismiss.jsx.map
