import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useCallback, useContext, useEffect, useMemo, useRef } from 'react';
import clsx from 'clsx';
import { isNaN } from '../utils/validate/number';
import { callInterceptor } from '../utils/interceptor';
import { addUnit, getSizeStyle, isDef, formatNumber, resetScroll, preventDefault } from '../utils';
import ConfigProviderContext from '../config-provider/ConfigProviderContext';
import { COMPONENT_TYPE_KEY } from '../utils/constant';
import useRefState from '../hooks/use-ref-state';
const LONG_PRESS_INTERVAL = 200;
const LONG_PRESS_START_TIME = 600;

function equal(value1, value2) {
  return String(value1) === String(value2);
} // add num and avoid float number


function add(num1, num2) {
  const cardinal = Math.pow(10, 10);
  return Math.round((num1 + num2) * cardinal) / cardinal;
}

const Stepper = props => {
  const {
    prefixCls,
    createNamespace
  } = useContext(ConfigProviderContext);
  const [bem] = createNamespace('stepper', prefixCls);
  const format = useCallback(value => {
    const {
      min,
      max,
      allowEmpty,
      decimalLength,
      integer
    } = props;

    if (allowEmpty && value === '') {
      return value;
    }

    value = formatNumber(String(value), !integer);
    value = value === '' ? 0 : +value;
    value = isNaN(value) ? +min : value;
    value = Math.max(Math.min(+max, value), +min); // format decimal

    if (isDef(decimalLength)) {
      value = value.toFixed(+decimalLength);
    }

    return value;
  }, [props.min, props.max, props.allowEmpty, props.decimalLength, props.integer]);

  const getInitialValue = () => {
    var _a;

    const defaultValue = (_a = props.value) !== null && _a !== void 0 ? _a : props.defaultValue;
    const value = format(defaultValue);
    return value;
  };

  let actionType;
  const inputRef = useRef(null);
  const innerEffectRef = useRef(false);
  const [current, setCurrent, currentRef] = useRefState(() => getInitialValue());
  const minusDisabled = useMemo(() => props.disabled || props.disableMinus || current <= +props.min, [props.disabled, props.disableMinus, props.min, current]);
  const plusDisabled = useMemo(() => props.disabled || props.disablePlus || current >= +props.max, [props.disabled, props.disablePlus, props.max, current]);
  const inputStyle = useMemo(() => ({
    width: addUnit(props.inputWidth),
    height: addUnit(props.buttonSize)
  }), [props.inputWidth, props.buttonSize]);
  const buttonStyle = useMemo(() => getSizeStyle(props.buttonSize), [props.buttonSize]);
  const innerChange = useCallback(value => {
    var _a;

    innerEffectRef.current = true;
    setCurrent(value);
    (_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, value);
  }, [props, setCurrent]);
  const check = useCallback(() => {
    const value = format(currentRef.current);

    if (!equal(value, currentRef.current)) {
      innerChange(value);
    }
  }, [format, currentRef.current]);

  const setValue = value => {
    if (props.beforeChange) {
      callInterceptor({
        args: [value],
        interceptor: props.beforeChange,

        done() {
          innerChange(value);
        }

      });
    } else {
      innerChange(value);
    }
  };

  const onChange = e => {
    var _a, _b, _c;

    const isMinus = actionType === 'minus';

    if (actionType === 'plus' && plusDisabled || isMinus && minusDisabled) {
      (_a = props.onOverlimit) === null || _a === void 0 ? void 0 : _a.call(props, actionType);
      return;
    }

    const diff = isMinus ? -props.step : +props.step;
    const value = format(add(+currentRef.current, diff));
    setValue(value);

    if (isMinus) {
      (_b = props.onMinus) === null || _b === void 0 ? void 0 : _b.call(props, e, value);
    } else {
      (_c = props.onPlus) === null || _c === void 0 ? void 0 : _c.call(props, e, value);
    }
  };

  const onInput = event => {
    const input = event.target;
    const {
      value
    } = input;
    const {
      decimalLength
    } = props;
    let formatted = formatNumber(String(value), !props.integer); // limit max decimal length

    if (isDef(decimalLength) && formatted.includes('.')) {
      const pair = formatted.split('.');
      formatted = `${pair[0]}.${pair[1].slice(0, +decimalLength)}`;
    } // prefer number type


    const isNumeric = formatted === String(+formatted);
    setValue(isNumeric ? +formatted : formatted);
  };

  const onFocus = useCallback(event => {
    var _a; // readonly not work in lagacy mobile safari


    if (props.disableInput && inputRef.current) {
      inputRef.current.blur();
    } else {
      (_a = props.onFocus) === null || _a === void 0 ? void 0 : _a.call(props, event);
    }
  }, [props]);

  const onBlur = event => {
    var _a;

    const input = event.target;
    const value = format(input.value);

    if (!equal(value, currentRef.current)) {
      innerChange(value);
    }

    (_a = props.onBlur) === null || _a === void 0 ? void 0 : _a.call(props, event);
    resetScroll();
  };

  const isLongPress = useRef(false);
  const longPressTimer = useRef(null);

  const longPressStep = () => {
    longPressTimer.current = setTimeout(() => {
      onChange();
      longPressStep();
    }, LONG_PRESS_INTERVAL);
  };

  const onTouchStart = () => {
    if (props.longPress) {
      isLongPress.current = false;
      clearTimeout(longPressTimer.current);
      longPressTimer.current = setTimeout(() => {
        isLongPress.current = true;
        onChange();
        longPressStep();
      }, LONG_PRESS_START_TIME);
    }
  };

  const onTouchEnd = event => {
    if (props.longPress) {
      clearTimeout(longPressTimer.current);

      if (isLongPress.current) {
        preventDefault(event);
      }
    }
  };

  const onMousedown = event => {
    // fix mobile safari page scroll down issue
    // see: https://github.com/youzan/vant/issues/7690
    if (props.disableInput) {
      event.preventDefault();
    }
  };

  const createListeners = type => ({
    onClick: event => {
      // disable double tap scrolling on mobile safari
      event.preventDefault();
      actionType = type;
      onChange(event);
    },
    onTouchStart: () => {
      actionType = type;
      onTouchStart();
    },
    onTouchEnd,
    onTouchCancel: onTouchEnd
  });

  useEffect(() => {
    if (innerEffectRef.current) {
      innerEffectRef.current = false;
      return;
    }

    setCurrent(props.value);
  }, [props.value, setCurrent]);
  useEffect(() => check, [props.max, props.min, props.integer, props.decimalLength]);
  return _jsxs("div", Object.assign({
    className: clsx(props.className, bem([props.theme])),
    style: props.style
  }, {
    children: [props.showMinus && _jsx("button", Object.assign({
      type: "button",
      "aria-label": "minus",
      style: buttonStyle,
      className: clsx(bem('minus', {
        disabled: minusDisabled
      }))
    }, createListeners('minus'))), props.showInput && _jsx("input", {
      ref: inputRef,
      type: props.integer ? 'tel' : 'text',
      role: "spinbutton",
      className: clsx(bem('input')),
      value: current !== null && current !== void 0 ? current : '',
      style: inputStyle,
      disabled: props.disabled,
      readOnly: props.disableInput,
      inputMode: props.integer ? 'numeric' : 'decimal',
      placeholder: props.placeholder,
      "aria-valuemax": +props.max,
      "aria-valuemin": +props.min,
      "aria-valuenow": +current,
      onChange: onInput,
      onBlur: onBlur,
      onFocus: onFocus,
      onMouseDown: onMousedown,
      onClick: props.onClick
    }), props.showPlus && _jsx("button", Object.assign({
      type: "button",
      "aria-label": "add",
      style: buttonStyle,
      className: clsx(bem('plus', {
        disabled: plusDisabled
      }))
    }, createListeners('plus')))]
  }));
};

Stepper.defaultProps = {
  theme: 'default',
  max: Number.MAX_VALUE,
  min: 1,
  step: 1,
  defaultValue: 1,
  showPlus: true,
  showMinus: true,
  showInput: true,
  longPress: true
};
export const STEPPER_KEY = Symbol('stepper');
const StepperNamespace = Object.assign(Stepper, {
  [COMPONENT_TYPE_KEY]: STEPPER_KEY
});
export default StepperNamespace;