'use client';

import * as React from 'react';
import { DEFAULT_STEP, MAX_POINTER_MOVES_AFTER_TOUCH, SCROLLING_POINTER_MOVE_DISTANCE, TOUCH_TIMEOUT } from "../utils/constants.js";
import { parseNumber } from "../utils/parse.js";
import { createChangeEventDetails, createGenericEventDetails } from "../../utils/createBaseUIEventDetails.js";
// Treat pen as touch-like to avoid forcing the software keyboard on stylus taps.
// Linux Chrome may emit "pen" historically for mouse usage due to a bug, but the touch path
// still works with minor behavioral differences.
function isTouchLikePointerType(pointerType) {
  return pointerType === 'touch' || pointerType === 'pen';
}
export function useNumberFieldButton(params) {
  const {
    allowInputSyncRef,
    disabled,
    formatOptionsRef,
    getStepAmount,
    id,
    incrementValue,
    inputRef,
    inputValue,
    intentionalTouchCheckTimeout,
    isIncrement,
    isPressedRef,
    locale,
    movesAfterTouchRef,
    readOnly,
    setValue,
    startAutoChange,
    stopAutoChange,
    valueRef,
    lastChangedValueRef,
    onValueCommitted
  } = params;
  const incrementDownCoordsRef = React.useRef({
    x: 0,
    y: 0
  });
  const isTouchingButtonRef = React.useRef(false);
  const ignoreClickRef = React.useRef(false);
  const pointerTypeRef = React.useRef('');
  const pressReason = isIncrement ? 'increment-press' : 'decrement-press';
  function commitValue(nativeEvent) {
    allowInputSyncRef.current = true;

    // The input may be dirty but not yet blurred, so the value won't have been committed.
    const parsedValue = parseNumber(inputValue, locale, formatOptionsRef.current);
    if (parsedValue !== null) {
      // The increment value function needs to know the current input value to increment it
      // correctly.
      valueRef.current = parsedValue;
      setValue(parsedValue, createChangeEventDetails(pressReason, nativeEvent, undefined, {
        direction: isIncrement ? 1 : -1
      }));
    }
  }
  const props = {
    disabled,
    'aria-readonly': readOnly || undefined,
    'aria-label': isIncrement ? 'Increase' : 'Decrease',
    'aria-controls': id,
    // Keyboard users shouldn't have access to the buttons, since they can use the input element
    // to change the value. On the other hand, `aria-hidden` is not applied because touch screen
    // readers should be able to use the buttons.
    tabIndex: -1,
    style: {
      WebkitUserSelect: 'none',
      userSelect: 'none'
    },
    onTouchStart() {
      isTouchingButtonRef.current = true;
    },
    onTouchEnd() {
      isTouchingButtonRef.current = false;
    },
    onClick(event) {
      const isDisabled = disabled || readOnly;
      if (event.defaultPrevented || isDisabled || (
      // If it's not a keyboard/virtual click, ignore.
      isTouchLikePointerType(pointerTypeRef.current) ? ignoreClickRef.current : event.detail !== 0)) {
        return;
      }
      commitValue(event.nativeEvent);
      const amount = getStepAmount(event) ?? DEFAULT_STEP;
      const prev = valueRef.current;
      incrementValue(amount, {
        direction: isIncrement ? 1 : -1,
        event: event.nativeEvent,
        reason: pressReason
      });
      const committed = lastChangedValueRef.current ?? valueRef.current;
      if (committed !== prev) {
        onValueCommitted(committed, createGenericEventDetails(pressReason, event.nativeEvent));
      }
    },
    onPointerDown(event) {
      const isMainButton = !event.button || event.button === 0;
      if (event.defaultPrevented || readOnly || !isMainButton || disabled) {
        return;
      }
      pointerTypeRef.current = event.pointerType;
      ignoreClickRef.current = false;
      isPressedRef.current = true;
      incrementDownCoordsRef.current = {
        x: event.clientX,
        y: event.clientY
      };
      commitValue(event.nativeEvent);
      const isTouchPointer = isTouchLikePointerType(event.pointerType);
      if (!isTouchPointer) {
        event.preventDefault();
        inputRef.current?.focus();
        startAutoChange(isIncrement, event);
      } else {
        // We need to check if the pointerdown was intentional, and not the result of a scroll
        // or pinch-zoom. In that case, we don't want to change the value.
        intentionalTouchCheckTimeout.start(TOUCH_TIMEOUT, () => {
          const moves = movesAfterTouchRef.current;
          movesAfterTouchRef.current = 0;
          // Only start auto-change if the touch is still pressed (prevents races
          // with pointerup occurring before the timeout fires on quick taps).
          const stillPressed = isPressedRef.current;
          if (stillPressed && moves != null && moves < MAX_POINTER_MOVES_AFTER_TOUCH) {
            startAutoChange(isIncrement, event);
            ignoreClickRef.current = true; // synthesized click should be ignored
          } else {
            // No auto-change (simple tap or scroll gesture), allow the click handler
            // to perform a single increment and commit.
            ignoreClickRef.current = false;
            stopAutoChange();
          }
        });
      }
    },
    onPointerUp(event) {
      // Ensure we mark the press as released for touch flows even if auto-change never started,
      // so the delayed auto-change check won’t start after a quick tap.
      if (isTouchLikePointerType(event.pointerType)) {
        isPressedRef.current = false;
      }
    },
    onPointerMove(event) {
      const isDisabled = disabled || readOnly;
      if (isDisabled || !isTouchLikePointerType(event.pointerType) || !isPressedRef.current) {
        return;
      }
      if (movesAfterTouchRef.current != null) {
        movesAfterTouchRef.current += 1;
      }
      const {
        x,
        y
      } = incrementDownCoordsRef.current;
      const dx = x - event.clientX;
      const dy = y - event.clientY;

      // An alternative to this technique is to detect when the NumberField's parent container
      // has been scrolled
      if (dx ** 2 + dy ** 2 > SCROLLING_POINTER_MOVE_DISTANCE ** 2) {
        stopAutoChange();
      }
    },
    onMouseEnter(event) {
      const isDisabled = disabled || readOnly;
      if (event.defaultPrevented || isDisabled || !isPressedRef.current || isTouchingButtonRef.current || isTouchLikePointerType(pointerTypeRef.current)) {
        return;
      }
      startAutoChange(isIncrement, event);
    },
    onMouseLeave() {
      if (isTouchingButtonRef.current) {
        return;
      }
      stopAutoChange();
    },
    onMouseUp() {
      if (isTouchingButtonRef.current) {
        return;
      }
      stopAutoChange();
    }
  };
  return props;
}