import React, { ChangeEvent, FC, useCallback, useState } from 'react';
import { useUpdateEffect } from 'react-use';
import styled from '@emotion/styled';
import Input, { InputProps } from '../input';

function isEmpty(value: number | string | undefined) {
  if ((!value && value !== 0) || !String(value).trim()) {
    return true;
  }

  return false;
}

function isInvalidate(value: number | string | undefined) {
  if (isEmpty(value) || Number.isNaN(Number(value))) {
    return true;
  }

  return false;
}

const StyledInput = styled(Input)`
  // 删除上下按钮
  &::-webkit-outer-spin-button,
  &::-webkit-inner-spin-button {
    -webkit-appearance: none !important;
  }

  &[type='number'] {
    -moz-appearance: textfield;
  }

  input::-webkit-outer-spin-button,
  input::-webkit-inner-spin-button {
    -webkit-appearance: none !important;
  }

  input[type='number'] {
    -moz-appearance: textfield;
  }
`;

export interface NumberInputProps
  extends Omit<InputProps, 'type' | 'value' | 'onChange' | 'onBlur' | 'defaultValue'> {
  /** 值 */
  value?: number | string;
  /** 精度 */
  precision?: number;
  /** 最小值 */
  min?: number;
  /** 最大值 */
  max?: number;
  /** 变化回调 */
  onChange?: (value?: number) => void;
  /** 失焦回调 */
  onBlur?: (value: string) => void;
}

const NumberInput: FC<NumberInputProps> = props => {
  const { min, max, precision = 0, value, onChange, onBlur, ...rest } = props;

  // 格式化，是否是有输入触发
  const formatter = useCallback(
    (newValue: number | string | undefined, userTyping: boolean) => {
      // 先转换成 string 类型
      const str = typeof newValue === 'number' ? String(newValue) : newValue;

      // 如果不是由输入触发并且是个值，失去焦点时会走进来格式化
      if (!userTyping && !isInvalidate(newValue)) {
        const numberNewValue = Number(newValue);
        if (typeof min === 'number' && numberNewValue < min) {
          return min.toFixed(precision);
        }

        if (typeof max === 'number' && numberNewValue > max) {
          return max.toFixed(precision);
        }

        return numberNewValue.toFixed(precision);
      }

      // 由输入触发
      return str;
    },
    [max, min, precision],
  );

  const [inputValue, setInternalInputValue] = useState(() => {
    if (isInvalidate(value)) {
      return '';
    }

    return formatter(value, false);
  });

  // 设置输入框的值和触发 onChange
  const setInputValue = useCallback(
    (newValue: number | string | undefined, userTyping: boolean): string | undefined => {
      const formatterValue = formatter(newValue, userTyping);
      setInternalInputValue(formatterValue);

      // 清空输入框时触发
      if (isEmpty(newValue) && formatterValue !== formatter(value, userTyping)) {
        onChange?.();
        return '';
      }

      if (!userTyping && !isInvalidate(newValue) && Number(value) !== Number(formatterValue)) {
        onChange?.(Number(formatterValue));
      }

      return formatterValue;
    },
    [formatter, onChange, value],
  );

  const handleChange = useCallback(
    (event: ChangeEvent<HTMLInputElement>) => {
      let val = event.target.value;
      // 中文句号转换
      val = val.replace(/。/g, '.');

      if (val === '') {
        setInputValue('', true);
        return;
      }

      if (new RegExp(`^(-|-?(\\d+|\\d?)\\.\\d{0,${precision}}|-?\\d+\\.?)$`).test(val)) {
        const formatValue = formatter(val, true);
        setInternalInputValue(formatValue);

        const numberInnerValue = Number(formatValue);
        if (
          onChange &&
          (typeof min !== 'number' || numberInnerValue >= min) &&
          (typeof max !== 'number' || numberInnerValue <= max)
        ) {
          onChange(numberInnerValue);
        }
      }
    },
    [formatter, max, min, onChange, precision, setInputValue],
  );

  // 失去焦点时格式化输入
  const handleBlur = useCallback(
    (event: ChangeEvent<HTMLInputElement>) => {
      // 如果输入框内容为空，则跳过格式化
      if (event.target.value === '') {
        onBlur?.('');
        return;
      }

      const val = setInputValue(event.target.value, false);
      onBlur?.(val as string);
    },
    [onBlur, setInputValue],
  );

  useUpdateEffect(() => {
    if (Number(value) !== Number(inputValue) || (inputValue === '' && Number(value) === 0)) {
      setInputValue(value, false);
    }
  }, [value]);

  return (
    <StyledInput
      {...rest}
      type="number"
      value={inputValue}
      onBlur={handleBlur}
      onChange={handleChange}
    />
  );
};

export default NumberInput;
