import baseComponent from '../helpers/baseComponent'
import classNames from '../helpers/classNames'

const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || Math.pow(2, 53) - 1

const toNumberWhenUserInput = (num) => {
    if (/\.\d*0$/.test(num) || num.length > 16) {
        return num
    }

    if (isNaN(num)) {
        return num
    }

    return Number(num)
}

const getValidValue = (value, min, max) => {
    let val = parseFloat(value)

    if (isNaN(val)) {
        return value
    }

    if (val < min) {
        val = min
    }

    if (val > max) {
        val = max
    }

    return val
}

baseComponent({
    externalClasses: ['wux-sub-class', 'wux-input-class', 'wux-add-class'],
    properties: {
        prefixCls: {
            type: String,
            value: 'wux-input-number',
        },
        shape: {
            type: String,
            value: 'square',
        },
        min: {
            type: Number,
            value: -MAX_SAFE_INTEGER,
        },
        max: {
            type: Number,
            value: MAX_SAFE_INTEGER,
        },
        step: {
            type: Number,
            value: 1,
        },
        defaultValue: {
            type: Number,
            value: 0,
        },
        value: {
            type: Number,
            value: 0,
            observer(newVal) {
                if (this.data.controlled) {
                    this.updated(newVal)
                }
            },
        },
        disabled: {
            type: Boolean,
            value: true,
        },
        longpress: {
            type: Boolean,
            value: false,
        },
        color: {
            type: String,
            value: 'balanced',
        },
        controlled: {
            type: Boolean,
            value: false,
        },
    },
    data: {
        inputValue: 0,
        disabledMin: false,
        disabledMax: false,
    },
    computed: {
        classes() {
            const { prefixCls, shape, color, disabledMin, disabledMax } = this.data
            const wrap = classNames(prefixCls, {
                [`${prefixCls}--${shape}`]: shape,
            })
            const sub = classNames(`${prefixCls}__selector`, {
                [`${prefixCls}__selector--sub`]: true,
                [`${prefixCls}__selector--${color}`]: color,
                [`${prefixCls}__selector--disabled`]: disabledMin,
            })
            const add = classNames(`${prefixCls}__selector`, {
                [`${prefixCls}__selector--add`]: true,
                [`${prefixCls}__selector--${color}`]: color,
                [`${prefixCls}__selector--disabled`]: disabledMax,
            })
            const icon = `${prefixCls}__icon`
            const input = `${prefixCls}__input`

            return {
                wrap,
                sub,
                add,
                icon,
                input,
            }
        },
    },
    methods: {
        /**
         * 更新值
         */
        updated(value, condition = true, trigger = false) {
            const { min, max } = this.data
            const inputValue = getValidValue(value, min, max)
            const disabledMin = inputValue <= min
            const disabledMax = inputValue >= max

            // 更新数值,判断最小或最大值禁用 sub 或 add 按钮
            if (condition) {
                this.setData({
                    inputValue,
                    disabledMin,
                    disabledMax,
                })
            }

            // 触发事件
            if (trigger) {
                this.triggerEvent('change', { value: inputValue })
            }
        },
        /**
         * 数字计算函数
         */
        calculation(type, meta) {
            const { disabledMax, disabledMin, inputValue, step, longpress, controlled } = this.data

            // add
            if (type === 'add') {
                if (disabledMax) return false
                this.updated(inputValue + step, !controlled, true)
            }

            // sub
            if (type === 'sub') {
                if (disabledMin) return false
                this.updated(inputValue - step, !controlled, true)
            }

            // longpress
            if (longpress && meta) {
                this.timeout = setTimeout(() => this.calculation(type, meta), 100)
            }
        },
        /**
         * 当键盘输入时,触发 input 事件
         */
        onInput(e) {
            this.clearInputTimer()
            this.inputTime = setTimeout(() => {
                const value = toNumberWhenUserInput(e.detail.value)
                this.updated(value, !this.data.controlled)
                this.triggerEvent('change', { value })
            }, 300)
        },
        /**
         * 输入框聚焦时触发
         */
        onFocus(e) {
            this.triggerEvent('focus', e.detail)
        },
        /**
         * 输入框失去焦点时触发
         */
        onBlur(e) {
            // always set input value same as value
            this.setData({
                inputValue: this.data.inputValue,
            })

            this.triggerEvent('blur', e.detail)
        },
        /**
         * 手指触摸后,超过350ms再离开
         */
        onLongpress(e) {
            const { type } = e.currentTarget.dataset
            const { longpress } = this.data
            if (longpress) {
                this.calculation(type, true)
            }
        },
        /**
         * 手指触摸后马上离开
         */
        onTap(e) {
            const { type } = e.currentTarget.dataset
            const { longpress } = this.data
            if (!longpress || longpress && !this.timeout) {
                this.calculation(type, false)
            }
        },
        /**
         * 	手指触摸动作结束
         */
        onTouchEnd() {
            this.clearTimer()
        },
        /**
         * 手指触摸动作被打断,如来电提醒,弹窗
         */
        onTouchCancel() {
            this.clearTimer()
        },
        /**
         * 清除长按的定时器
         */
        clearTimer() {
            if (this.timeout) {
                clearTimeout(this.timeout)
                this.timeout = null
            }
        },
        /**
         * 清除输入框的定时器
         */
        clearInputTimer() {
            if (this.inputTime) {
                clearTimeout(this.inputTime)
                this.inputTime = null
            }
        },
    },
    attached() {
        const { defaultValue, value, controlled } = this.data
        const inputValue = controlled ? value : defaultValue

        this.updated(inputValue)
    },
    detached() {
        this.clearTimer()
        this.clearInputTimer()
    },
})