import { jsx as _jsx } from "react/jsx-runtime";
import { useContext, useMemo, useRef, useState } from 'react';
import cls from 'clsx';
import { addUnit, range, addNumber, preventDefault, getSizeStyle, stopPropagation } from '../utils';
import { getRect } from '../hooks/use-rect';
import useEventListener from '../hooks/use-event-listener';
import ConfigProviderContext from '../config-provider/ConfigProviderContext';
import { useTouch } from '../hooks';

const Slider = props => {
  const {
    prefixCls,
    createNamespace
  } = useContext(ConfigProviderContext);
  const [bem] = createNamespace('slider', prefixCls);
  const [buttonRef1, setButtonRef1] = useState(null);
  const [buttonRef2, setButtonRef2] = useState(null);
  const buttonIndex = useRef();
  const startValue = useRef();
  const currentValue = useRef(props.value);
  const root = useRef();
  const dragStatus = useRef();
  const touch = useTouch();
  const scope = useMemo(() => Number(props.max) - Number(props.min), [props.max, props.min]);
  const wrapperStyle = useMemo(() => {
    const crossAxis = props.vertical ? 'width' : 'height';
    return Object.assign({
      background: props.inactiveColor,
      [crossAxis]: addUnit(props.barHeight)
    }, props.style);
  }, [props.vertical, props.barHeight, props.inactiveColor, props.style]);

  const isRange = val => props.range && Array.isArray(val); // 计算选中条的长度百分比


  const calcMainAxis = () => {
    const {
      value,
      min
    } = props;

    if (isRange(value)) {
      return `${(value[1] - value[0]) * 100 / scope}%`;
    }

    return `${(+value - Number(min)) * 100 / scope}%`;
  }; // 计算选中条的开始位置的偏移量


  const calcOffset = () => {
    const {
      value,
      min
    } = props;

    if (isRange(value)) {
      return `${(value[0] - Number(min)) * 100 / scope}%`;
    }

    return '0%';
  };

  const barStyle = useMemo(() => {
    const mainAxis = props.vertical ? 'height' : 'width';
    const style = {
      [mainAxis]: calcMainAxis(),
      background: props.activeColor
    };

    if (dragStatus.current) {
      style.transition = 'none';
    }

    const getPositionKey = () => {
      if (props.vertical) {
        return props.reverse ? 'bottom' : 'top';
      }

      return props.reverse ? 'right' : 'left';
    };

    style[getPositionKey()] = calcOffset();
    return style; // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [calcOffset]);

  const format = value => {
    const min = +props.min;
    const max = +props.max;
    const step = +props.step;
    value = range(value, min, max);
    const diff = Math.round((value - min) / step) * step;
    return addNumber(min, diff);
  };

  const isSameValue = (newValue, oldValue) => JSON.stringify(newValue) === JSON.stringify(oldValue);

  const handleRangeValue = value => {
    var _a, _b; // 设置默认值


    const left = (_a = value[0]) !== null && _a !== void 0 ? _a : Number(props.min);
    const right = (_b = value[1]) !== null && _b !== void 0 ? _b : Number(props.max); // 处理两个滑块重叠之后的情况

    return left > right ? [right, left] : [left, right];
  };

  const updateValue = (value, end) => {
    var _a, _b;

    if (isRange(value)) {
      value = handleRangeValue(value).map(format);
    } else {
      value = format(value);
    }

    if (!isSameValue(value, props.value)) {
      (_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, value);
    }

    if (end && !isSameValue(value, startValue.current)) {
      (_b = props.onChangeAfter) === null || _b === void 0 ? void 0 : _b.call(props, value);
    }

    return value;
  };

  const onClick = event => {
    event.stopPropagation();

    if (props.disabled || props.readonly) {
      return;
    }

    const {
      min,
      reverse,
      vertical,
      value: modelValue
    } = props;
    const rect = getRect(root.current);

    const getDelta = () => {
      if (vertical) {
        if (reverse) {
          return rect.bottom - event.clientY;
        }

        return event.clientY - rect.top;
      }

      if (reverse) {
        return rect.right - event.clientX;
      }

      return event.clientX - rect.left;
    };

    const total = vertical ? rect.height : rect.width;
    const value = Number(min) + getDelta() / total * scope;

    if (isRange(modelValue)) {
      const [left, right] = modelValue;
      const middle = (left + right) / 2;

      if (value <= middle) {
        updateValue([value, right], true);
      } else {
        updateValue([left, value], true);
      }
    } else {
      updateValue(value, true);
    }
  };

  const onTouchStart = event => {
    if (props.disabled || props.readonly) {
      return;
    }

    touch.start(event);
    currentValue.current = JSON.parse(JSON.stringify(props.value));

    if (isRange(currentValue.current)) {
      startValue.current = currentValue.current.map(format);
    } else {
      startValue.current = format(currentValue.current);
    }

    dragStatus.current = 'start';
  };

  const onTouchMove = event => {
    var _a;

    if (props.disabled || props.readonly) {
      return;
    }

    if (dragStatus.current === 'start') {
      (_a = props.onDragStart) === null || _a === void 0 ? void 0 : _a.call(props, event, startValue.current);
    }

    preventDefault(event, true);
    touch.move(event);
    dragStatus.current = 'dragging';
    const rect = getRect(root.current);
    const delta = props.vertical ? touch.deltaY.current : touch.deltaX.current;
    const total = props.vertical ? rect.height : rect.width;
    let diff = delta / total * scope;

    if (props.reverse) {
      diff = -diff;
    }

    if (isRange(startValue.current)) {
      const index = props.reverse ? 1 - buttonIndex.current : buttonIndex.current;
      currentValue.current[index] = startValue.current[index] + diff;
    } else {
      currentValue.current = +startValue.current + diff;
    }

    updateValue(currentValue.current);
  };

  const onTouchEnd = event => {
    var _a;

    if (props.disabled || props.readonly) {
      return;
    }

    if (dragStatus.current === 'dragging') {
      const value = updateValue(currentValue.current, true);
      (_a = props.onDragEnd) === null || _a === void 0 ? void 0 : _a.call(props, event, value);
    }

    dragStatus.current = '';
  };

  const getButtonClassName = index => {
    if (typeof index === 'number') {
      const position = ['left', 'right'];
      return bem('button-wrapper', position[index]);
    }

    return bem('button-wrapper', props.reverse ? 'left' : 'right');
  };

  const renderButtonContent = (value, index) => {
    if (typeof index === 'number') {
      const slot = props[index === 0 ? 'leftButton' : 'rightButton'];

      if (slot) {
        return slot;
      }
    }

    if (typeof props.button === 'function') {
      return props.button({
        value
      });
    }

    if (props.button) {
      return props.button;
    }

    return _jsx("div", {
      className: cls(bem('button')),
      style: getSizeStyle(props.buttonSize)
    });
  };

  const renderButton = (buttounRef, index) => {
    const value = typeof index === 'number' ? props.value[index] : props.value;
    return _jsx("div", Object.assign({
      ref: buttounRef,
      role: "slider",
      className: cls(getButtonClassName(index)),
      tabIndex: props.disabled || props.readonly ? -1 : 0,
      "aria-valuemin": props.min,
      "aria-valuenow": value,
      "aria-valuemax": props.max,
      "aria-orientation": props.vertical ? 'vertical' : 'horizontal',
      onTouchStart: event => {
        if (typeof index === 'number') {
          // save index of current button
          buttonIndex.current = index;
        }

        onTouchStart(event);
      },
      onTouchEnd: onTouchEnd,
      onTouchCancel: onTouchEnd,
      onClick: stopPropagation
    }, {
      children: renderButtonContent(currentValue.current, index)
    }), index);
  };

  useEventListener('touchmove', onTouchMove, {
    target: buttonRef1,
    depends: [touch.deltaX.current, touch.deltaY.current, props.disabled, props.readonly]
  });
  useEventListener('touchmove', onTouchMove, {
    target: buttonRef2,
    depends: [touch.deltaX.current, touch.deltaY.current, props.disabled, props.readonly]
  });
  return _jsx("div", Object.assign({
    ref: root,
    style: wrapperStyle,
    className: cls(props.className, bem({
      vertical: props.vertical,
      disabled: props.disabled
    })),
    onClick: onClick
  }, {
    children: _jsx("div", Object.assign({
      className: cls(bem('bar')),
      style: barStyle
    }, {
      children: props.range ? [renderButton(setButtonRef1, 0), renderButton(setButtonRef2, 1)] : renderButton(setButtonRef1)
    }))
  }));
};

Slider.defaultProps = {
  min: 0,
  max: 100,
  step: 1
};
export default Slider;