import baseComponent from '../helpers/baseComponent'
import classNames from '../helpers/libs/classNames'
import locales from './locales/index'
import { props } from './props'

const DATETIME = 'datetime'
const DATE = 'date'
const TIME = 'time'
const MONTH = 'month'
const YEAR = 'year'
const ONE_DAY = 24 * 60 * 60 * 1000

function fomartArray(min, max, step = 1) {
    let i = min
    let result = []
    while (i <= max) {
        result.push(i)
        i+=step
    }
    return result
}

function getDaysInMonth(date) {
    return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate()
}

function pad(n) {
    return n < 10 ? `0${n}` : n + ''
}

function cloneDate(date) {
    return new Date(+date)
}

function setMonth(date, month) {
    date.setDate(Math.min(date.getDate(), getDaysInMonth(new Date(date.getFullYear(), month))))
    date.setMonth(month)
}

function valueToDate(value, props = {}) {
    if (!Array.isArray(value)) {
        if (typeof value === 'string') {
            value = value.replace(/\-/g, '/')
        }
        if (!isNaN(Number(value))) {
            value = Number(value)
        }
        return new Date(value)
    }

    const { mode, use12Hours } = props
    const now = new Date()
    const year = now.getFullYear()
    const month = now.getMonth()
    const day = now.getDate()
    const newValue = value.map((v) => Number(v))
    if (use12Hours && [DATETIME, TIME].includes(mode)) {
        const hourIndex = mode === DATETIME ? 3 : 0
        const ampmIndex = newValue.length - 1
        const ampm = Number(newValue[ampmIndex])
        let nhour = Number(newValue[hourIndex])

        if (ampm === 1) {
            if (nhour <= 12) {
                nhour += 12
            }
            nhour = nhour >= 24 ? 0 : nhour
        } else {
            if (nhour === 0) {
                nhour = 12
            }
            if (nhour > 12) {
                nhour -= 12
            }
            nhour = nhour >= 12 ? 0 : nhour
        }

        newValue.splice(hourIndex, 1, nhour)
        newValue.splice(ampmIndex, 1)
    }
    if (mode === TIME) {
        newValue.unshift(day)
        newValue.unshift(month)
        newValue.unshift(year)
    } else if (mode === MONTH) {
        newValue.push(day)
    } else if (mode === YEAR) {
        newValue.push(month)
        newValue.push(day)
    }
    while (newValue.length <= 6) {
        newValue.push(0)
    }
    return new Date(...newValue)
}

baseComponent({
    properties: props,
    data: {
        inputValue: [],
        options: [],
    },
    observers: {
        inputValue() {
            this.updatedCols()
        },
        value(value) {
            this.setValue(value)
        },
        ['mode, minuteStep, use12Hours, minDate, maxDate, minHour, maxHour, minMinute, maxMinute, lang']() {
            this.setValue(this.data.inputValue)
        },
    },
    methods: {
        getDefaultMinDate() {
            if (!this.defaultMinDate) {
                this.defaultMinDate = new Date(2000, 1, 1, 0, 0, 0)
            }
            return this.defaultMinDate
        },
        getDefaultMaxDate() {
            if (!this.defaultMaxDate) {
                this.defaultMaxDate = new Date(2030, 1, 1, 23, 59, 59)
            }
            return this.defaultMaxDate
        },
        getMinDate() {
            return this.data.minDate ? valueToDate(this.data.minDate, this.data) : this.getDefaultMinDate()
        },
        getMaxDate() {
            return this.data.maxDate ? valueToDate(this.data.maxDate, this.data) : this.getDefaultMaxDate()
        },
        getDateMember(type = 'min', member = 'year') {
            const methods = {
                min: 'getMinDate',
                max: 'getMaxDate',
                year: 'getFullYear',
                month: 'getMonth',
                day: 'getDate',
                hour: 'getHours',
                minute: 'getMinutes',
            }
            return this[methods[type]]()[methods[member]]()
        },
        getDisplayHour(rawHour) {
            // 12 hour am (midnight 00:00) -> 12 hour pm (noon 12:00) -> 12 hour am (midnight 00:00)
            if (this.data.use12Hours) {
                if (rawHour === 0) {
                    rawHour = 12
                }
                if (rawHour > 12) {
                    rawHour -= 12
                }
                return rawHour
            }
            return rawHour
        },
        setHours(date, hour) {
            if (this.data.use12Hours) {
                const dh = date.getHours()
                let nhour = hour
                nhour = dh >= 12 ? hour + 12 : hour
                nhour = nhour >= 24 ? 0 : nhour // Make sure no more than one day
                date.setHours(nhour)
            } else {
                date.setHours(hour)
            }
        },
        setAmPm(date, index) {
            if (index === 0) {
                date.setTime(+date - ONE_DAY / 2)
            } else {
                date.setTime(+date + ONE_DAY / 2)
            }
        },
        getNewDate(values, index) {
            const value = parseInt(values[index], 10)
            const { mode } = this.data
            let newValue = cloneDate(this.getDate())
            if (mode === DATETIME || mode === DATE || mode === YEAR || mode === MONTH) {
                switch (index) {
                        case 0:
                            newValue.setFullYear(value)
                            break
                        case 1:
                            setMonth(newValue, value)
                            break
                        case 2:
                            newValue.setDate(value)
                            break
                        case 3:
                            this.setHours(newValue, value)
                            break
                        case 4:
                            newValue.setMinutes(value)
                            break
                        case 5:
                            this.setAmPm(newValue, value)
                            break
                        default:
                            break
                }
            } else if (mode === TIME) {
                switch (index) {
                        case 0:
                            this.setHours(newValue, value)
                            break
                        case 1:
                            newValue.setMinutes(value)
                            break
                        case 2:
                            this.setAmPm(newValue, value)
                            break
                        default:
                            break
                }
            }
            return this.clipDate(newValue)
        },
        clipDate(date) {
            const { mode } = this.data
            const minDate = this.getMinDate()
            const maxDate = this.getMaxDate()
            if (mode === DATETIME) {
                if (date < minDate) {
                    return cloneDate(minDate)
                }
                if (date > maxDate) {
                    return cloneDate(maxDate)
                }
            } else if (mode === DATE || mode === YEAR || mode === MONTH) {
                if (+date + ONE_DAY <= minDate) {
                    return cloneDate(minDate)
                }
                if (date >= +maxDate + ONE_DAY) {
                    return cloneDate(maxDate)
                }
            } else if (mode === TIME) {
                const maxHour = maxDate.getHours()
                const maxMinutes = maxDate.getMinutes()
                const minHour = minDate.getHours()
                const minMinutes = minDate.getMinutes()
                const hour = date.getHours()
                const minutes = date.getMinutes()
                if (hour < minHour || hour === minHour && minutes < minMinutes) {
                    return cloneDate(minDate)
                }
                if (hour > maxHour || hour === maxHour && minutes > maxMinutes) {
                    return cloneDate(maxDate)
                }
            }
            return date
        },
        getDate(d) {
            const date = d ? d : this.data.value
            return this.clipDate(date ? valueToDate(date, this.data) : this.getMinDate())
        },
        getDateData(date) {
            const { mode, lang } = this.data
            const locale = locales[lang]
            const selYear = date.getFullYear()
            const selMonth = date.getMonth()

            const minDateYear = this.getDateMember('min', 'year')
            const maxDateYear = this.getDateMember('max', 'year')
            const minDateMonth = this.getDateMember('min', 'month')
            const maxDateMonth = this.getDateMember('max', 'month')
            const minDateDay = this.getDateMember('min', 'day')
            const maxDateDay = this.getDateMember('max', 'day')

            const years = fomartArray(minDateYear, maxDateYear).map((i) => ({
                value: i + '',
                label: i + locale.year + '',
            }))

            if (mode === YEAR) {
                return [years]
            }

            const minMonth = minDateYear === selYear ? minDateMonth : 0
            const maxMonth = maxDateYear === selYear ? maxDateMonth :  11
            const months = fomartArray(minMonth, maxMonth).map((i) => ({
                value: i + '',
                label: i + 1 + locale.month + '',
            }))

            if (mode === MONTH) {
                return [years, months]
            }

            const minDay = minDateYear === selYear && minDateMonth === selMonth ? minDateDay : 1
            const maxDay = maxDateYear === selYear && maxDateMonth === selMonth ? maxDateDay : getDaysInMonth(date)

            const days = fomartArray(minDay, maxDay).map((i) => ({
                value: i + '',
                label: i + locale.day + '',
            }))

            return [years, months, days]
        },
        getTimeData(date) {
            let { minHour, maxHour, minMinute, maxMinute } = this.data
            const { mode, minuteStep, use12Hours, lang } = this.data
            const locale = locales[lang]
            const minDateMinute = this.getDateMember('min', 'minute')
            const maxDateMinute = this.getDateMember('max', 'minute')
            const minDateHour = this.getDateMember('min', 'hour')
            const maxDateHour = this.getDateMember('max', 'hour')
            const hour = date.getHours()

            if (mode === DATETIME) {
                const year = date.getFullYear()
                const month = date.getMonth()
                const day = date.getDate()
                const minDateYear = this.getDateMember('min', 'year')
                const maxDateYear = this.getDateMember('max', 'year')
                const minDateMonth = this.getDateMember('min', 'month')
                const maxDateMonth = this.getDateMember('max', 'month')
                const minDateDay = this.getDateMember('min', 'day')
                const maxDateDay = this.getDateMember('max', 'day')
                if (minDateYear === year && minDateMonth === month && minDateDay === day) {
                    minHour = minDateHour
                    if (minDateHour === hour) {
                        minMinute = minDateMinute
                    }
                }
                if (maxDateYear === year && maxDateMonth === month && maxDateDay === day) {
                    maxHour = maxDateHour
                    if (maxDateHour === hour) {
                        maxMinute = maxDateMinute
                    }
                }
            } else {
                minHour = minDateHour
                if (minDateHour === hour) {
                    minMinute = minDateMinute
                }
                maxHour = maxDateHour
                if (maxDateHour === hour) {
                    maxMinute = maxDateMinute
                }
            }

            let hours = []
            if (minHour === 0 && maxHour === 0 || minHour !== 0 && maxHour !== 0) {
                minHour = this.getDisplayHour(minHour)
            } else if (minHour === 0 && use12Hours) {
                minHour = 1
                hours.push({
                    value: '0',
                    label: locale.hour ? '12' + locale.hour : '12',
                })
            }
            maxHour = this.getDisplayHour(maxHour)
            hours = [...hours, ...fomartArray(minHour, maxHour).map((i) => ({
                value: i + '',
                label: locale.hour ? i + locale.hour + '' : pad(i),
            }))]

            const minutes = []
            const selMinute = date.getMinutes()
            for (let i = minMinute; i <= maxMinute; i += minuteStep) {
                minutes.push({
                    value: i + '',
                    label: locale.minute ? i + locale.minute + '' : pad(i),
                })
                if (selMinute > i && selMinute < i + minuteStep) {
                    minutes.push({
                        value: selMinute + '',
                        label: locale.minute ? selMinute + locale.minute + '' : pad(selMinute),
                    })
                }
            }

            const ampm = [{ value: '0', label: locale.am }, { value: '1', label: locale.pm }]

            return [hours, minutes].concat(use12Hours ? [ampm] : [])
        },
        getValueCols(d) {
            const { mode, use12Hours } = this.data
            const date = this.getDate(d)
            let cols = []
            let value = []

            if (mode === YEAR) {
                return {
                    cols: this.getDateData(date),
                    value: [date.getFullYear() + ''],
                }
            }

            if (mode === MONTH) {
                return {
                    cols: this.getDateData(date),
                    value: [date.getFullYear() + '', date.getMonth() + ''],
                }
            }

            if (mode === DATETIME || mode === DATE) {
                cols = this.getDateData(date)
                value = [date.getFullYear() + '', date.getMonth() + '', date.getDate() + '']
            }

            if (mode === DATETIME || mode === TIME) {
                cols = cols.concat(this.getTimeData(date))
                const hour = date.getHours()
                const selMinute = date.getMinutes()
                let dtValue = [hour + '', selMinute + '']
                let nhour = hour
                if (use12Hours) {
                    nhour = hour === 0 ? 12 : (hour > 12 ? hour - 12 : hour)
                    dtValue = [nhour + '', selMinute + '', (hour >= 12 ? 1 : 0) + '']
                }
                value = value.concat(dtValue)
            }

            return {
                value,
                cols,
            }
        },
        onValueChange(e) {
            const { value, index } = e.detail
            const newDate = this.getNewDate(value, index)
            const { value: newValue, cols: newCols } = this.getValueCols(newDate)
            const values = this.getValue(newValue, newCols)
            this.triggerEvent('valueChange', { ...e.detail, ...values, date: +newDate })
        },
        updatedCols() {
            const { cols } = this.getValueCols()
            this.setData({ cols })
        },
        updated(inputValue) {
            if (this.data.inputValue !== inputValue) {
                this.setData({
                    inputValue,
                })
            }
        },
        setValue(value = this.data.inputValue) {
            const { value: inputValue } = this.getValueCols()
            this.updated(inputValue)
        },
        getValue(value = this.data.inputValue, cols = this.data.cols) {
            this.picker = this.picker || this.querySelector('#wux-picker')
            return {
                ...this.picker.getValue(value, cols),
                date: +this.getDate(),
            }
        },
    },
    attached() {
        this.setValue(this.data.value)
    },
})






















