import Day from './day'
import Todo from './todo'
import WxData from './wxData'
import convertSolarLunar from './convertSolarLunar'
import {
    Logger,
    GetDate,
    delRepeatedEnableDay,
    getDateTimeStamp,
    converEnableDaysToTimestamp
} from './utils'

const getDate = new GetDate()
const logger = new Logger()

class Calendar extends WxData {
    constructor(component) {
        super(component)
        this.Component = component
    }

    getCalendarConfig() {
        return this.Component.config
    }

    /**
     * 渲染日历
     * @param {number} curYear 年份
     * @param {number} curMonth  月份
     * @param {number} curDate  日期
     * @param {boolean} disableSelect 是否禁用选中
     */
    renderCalendar(curYear, curMonth, curDate, disableSelect) {
        return new Promise(resolve => {
            const config = this.getCalendarConfig()
            this.calculateEmptyGrids(curYear, curMonth)
            this.calculateDays(curYear, curMonth, curDate, disableSelect).then(() => {
                const {todoLabels, specialStyleDates, enableDays, selectedDay} =
                this.getData('calendar') || {}
                if (
                    todoLabels &&
                    todoLabels.find(
                        item => +item.month === +curMonth && +item.year === +curYear
                    )
                ) {
                    Todo(this.Component).setTodoLabels()
                }
                if (
                    specialStyleDates &&
                    specialStyleDates.length &&
                    specialStyleDates.find(
                        item => +item.month === +curMonth && +item.year === +curYear
                    )
                ) {
                    Day(this.Component).setDateStyle(specialStyleDates)
                }

                if (
                    enableDays &&
                    enableDays.length &&
                    enableDays.find(item => {
                        let ymd = item.split('-')
                        return +ymd[1] === +curMonth && +ymd[0] === +curYear
                    })
                ) {
                    Day(this.Component).enableDays(enableDays)
                }

                if (
                    selectedDay &&
                    selectedDay.length &&
                    selectedDay.find(
                        item => +item.month === +curMonth && +item.year === +curYear
                    ) &&
                    config.mulit
                ) {
                    Day(this.Component).setSelectedDays(selectedDay)
                }

                if (!this.Component.firstRender) {
                    resolve({
                        firstRender: true
                    })
                } else {
                    resolve({
                        firstRender: false
                    })
                }
            })
        })
    }

    /**
     * 计算当前月份前后两月应占的格子
     * @param {number} year 年份
     * @param {number} month 月份
     */
    calculateEmptyGrids(year, month) {
        this.calculatePrevMonthGrids(year, month)
        this.calculateNextMonthGrids(year, month)
    }

    /**
     * 计算上月应占的格子
     * @param {number} year 年份
     * @param {number} month 月份
     */
    calculatePrevMonthGrids(year, month) {
        let empytGrids = []
        const prevMonthDays = getDate.thisMonthDays(year, month - 1)
        let firstDayOfWeek = getDate.firstDayOfWeek(year, month)
        const config = this.getCalendarConfig() || {}
        if (config.firstDayOfWeek === 'Mon') {
            if (firstDayOfWeek === 0) {
                firstDayOfWeek = 6
            } else {
                firstDayOfWeek -= 1
            }
        }
        if (firstDayOfWeek > 0) {
            const len = prevMonthDays - firstDayOfWeek
            const {onlyShowCurrentMonth} = config
            const {showLunar} = this.getCalendarConfig()
            for (let i = prevMonthDays; i > len; i--) {
                if (onlyShowCurrentMonth) {
                    empytGrids.push('')
                } else {
                    empytGrids.push({
                        day: i,
                        lunar: showLunar
                            ? convertSolarLunar.solar2lunar(year, month - 1, i)
                            : null
                    })
                }
            }
            this.setData({
                'calendar.empytGrids': empytGrids.reverse()
            })
        } else {
            this.setData({
                'calendar.empytGrids': null
            })
        }
    }

    /**
     * 计算下一月日期是否需要多展示的日期
     * 某些月份日期为5排,某些月份6排,统一为6排
     * @param {number} year
     * @param {number} month
     * @param {object} config
     */
    calculateExtraEmptyDate(year, month, config) {
        let extDate = 0
        if (+month === 2) {
            extDate += 7
            let firstDayofMonth = getDate.dayOfWeek(year, month, 1)
            if (config.firstDayOfWeek === 'Mon') {
                if (+firstDayofMonth === 1) extDate += 7
            } else {
                if (+firstDayofMonth === 0) extDate += 7
            }
        } else {
            let firstDayofMonth = getDate.dayOfWeek(year, month, 1)
            if (config.firstDayOfWeek === 'Mon') {
                if (firstDayofMonth !== 0 && firstDayofMonth < 6) {
                    extDate += 7
                }
            } else {
                if (firstDayofMonth <= 5) {
                    extDate += 7
                }
            }
        }
        return extDate
    }

    /**
     * 计算下月应占的格子
     * @param {number} year 年份
     * @param {number} month  月份
     */
    calculateNextMonthGrids(year, month) {
        let lastEmptyGrids = []
        const thisMonthDays = getDate.thisMonthDays(year, month)
        let lastDayWeek = getDate.dayOfWeek(year, month, thisMonthDays)
        const config = this.getCalendarConfig() || {}
        if (config.firstDayOfWeek === 'Mon') {
            if (lastDayWeek === 0) {
                lastDayWeek = 6
            } else {
                lastDayWeek -= 1
            }
        }
        let len = 7 - (lastDayWeek + 1)
        const {onlyShowCurrentMonth, showLunar} = config
        if (!onlyShowCurrentMonth) {
            len = len + this.calculateExtraEmptyDate(year, month, config)
        }
        for (let i = 1; i <= len; i++) {
            if (onlyShowCurrentMonth) {
                lastEmptyGrids.push('')
            } else {
                lastEmptyGrids.push({
                    day: i,
                    lunar: showLunar
                        ? convertSolarLunar.solar2lunar(year, month + 1, i)
                        : null
                })
            }
        }
        this.setData({
            'calendar.lastEmptyGrids': lastEmptyGrids
        })
    }

    /**
     * 日历初始化将默认值写入 selectDay
     * @param {number} year
     * @param {number} month
     * @param {number} curDate
     */
    setSelectedDay(year, month, curDate) {
        let selectedDay = []
        const config = this.getCalendarConfig()
        if (config.noDefault) {
            selectedDay = []
            config.noDefault = false
        } else {
            const data = this.getData('calendar') || {}
            const {showLunar} = this.getCalendarConfig()
            selectedDay = curDate
                ? [
                    {
                        year,
                        month,
                        day: curDate,
                        choosed: true,
                        week: getDate.dayOfWeek(year, month, curDate),
                        lunar: showLunar
                            ? convertSolarLunar.solar2lunar(year, month, curDate)
                            : null
                    }
                ]
                : data.selectedDay
        }
        return selectedDay
    }

    __getDisableDateTimestamp() {
        let disableDateTimestamp
        const {date, type} = this.getCalendarConfig().disableMode || {}
        if (date) {
            const t = date.split('-')
            if (t.length < 3) {
                logger.warn('配置 disableMode.date 格式错误')
                return {}
            }
            disableDateTimestamp = getDateTimeStamp({
                year: +t[0],
                month: +t[1],
                day: +t[2]
            })
        }
        return {
            disableDateTimestamp,
            disableType: type
        }
    }

    resetDates() {
        this.setData({
            'calendar.days': []
        })
    }

    /**
     * 设置日历面板数据
     * @param {number} year 年份
     * @param {number} month  月份
     * @param {number} curDate  日期
     * @param {boolean} disableSelect 是否禁用选中
     */
    calculateDays(year, month, curDate, disableSelect) {
        return new Promise(resolve => {
            // 避免切换日期时样式残影
            this.resetDates()
            let days = []
            const {
                disableDays = [],
                chooseAreaTimestamp = [],
                selectedDay: selectedDates = []
            } = this.getData('calendar')
            days = Day(this.Component).buildDate(year, month)
            let selectedDay = selectedDates
            if (!disableSelect) {
                selectedDay = this.setSelectedDay(year, month, curDate)
            }
            const selectedDayStr = selectedDay.map(d => getDate.toTimeStr(d))
            const disableDaysStr = disableDays.map(d => getDate.toTimeStr(d))
            const [areaStart, areaEnd] = chooseAreaTimestamp
            days.forEach(item => {
                const cur = getDate.toTimeStr(item)
                const timestamp = getDateTimeStamp(item)
                if (selectedDayStr.includes(cur) && !disableSelect) {
                    item.choosed = true
                    if (timestamp > areaEnd || timestamp < areaStart) {
                        const idx = selectedDay.findIndex(
                            selectedDate =>
                                getDate.toTimeStr(selectedDate) === getDate.toTimeStr(item)
                        )
                        selectedDay.splice(idx, 1)
                    }
                } else if (
                    areaStart &&
                    areaEnd &&
                    timestamp >= areaStart &&
                    timestamp <= areaEnd &&
                    !disableSelect
                ) {
                    item.choosed = true
                    selectedDay.push(item)
                }
                if (disableDaysStr.includes(cur)) item.disable = true

                const {
                    disableDateTimestamp,
                    disableType
                } = this.__getDisableDateTimestamp()
                let disabelByConfig = false
                if (disableDateTimestamp) {
                    if (
                        (disableType === 'before' && timestamp < disableDateTimestamp) ||
                        (disableType === 'after' && timestamp > disableDateTimestamp)
                    ) {
                        disabelByConfig = true
                    }
                }
                const isDisable = disabelByConfig || this.__isDisable(timestamp)
                if (isDisable) {
                    item.disable = true
                    item.choosed = false
                }
            })
            this.setData(
                {
                    'calendar.days': days,
                    'calendar.selectedDay': [...selectedDay] || []
                },
                () => {
                    resolve()
                }
            )
        })
    }

    __isDisable(timestamp) {
        const {
            enableArea = [],
            enableDays = [],
            enableAreaTimestamp = []
        } = this.getData('calendar')
        let setDisable = false
        let expectEnableDaysTimestamp = converEnableDaysToTimestamp(enableDays)
        if (enableArea.length) {
            expectEnableDaysTimestamp = delRepeatedEnableDay(enableDays, enableArea)
        }
        if (enableAreaTimestamp.length) {
            if (
                (+enableAreaTimestamp[0] > +timestamp ||
                    +timestamp > +enableAreaTimestamp[1]) &&
                !expectEnableDaysTimestamp.includes(+timestamp)
            ) {
                setDisable = true
            }
        } else if (
            expectEnableDaysTimestamp.length &&
            !expectEnableDaysTimestamp.includes(+timestamp)
        ) {
            setDisable = true
        }
        return setDisable
    }
}

export default component => new Calendar(component)