import { EventFreq } from '@/constant'
import {
  ONE_DAY,
  toDate,
  toZeroPoint,
  pickMin,
  pickMax,
  isOverDay,
  isIn
} from '@/utils/event'
import scheduleMinxi from './schedule'
import eventMinxi from './event'

export default {
  mixins: [scheduleMinxi, eventMinxi],
  data () {
    return {
      mode: 1,
      weeks: [],
      weekIndex: 0,
      minDate: null,
      maxDate: null,
      current: null
    }
  },
  computed: {
    activeComponent () {
      switch (this.mode) {
        case 0:
          return 'ScheduleCalendarWeek'
        default:
          return 'ScheduleCalendarMonth'
      }
    },
    date () {
      if (!this.current) {
        return ''
      }
      if (this.mode === 0) {
        const week = this.weeks[this.weekIndex]
        const sun = week[0]
        const sunYear = sun.date.getFullYear()
        const sunMonth = sun.date.getMonth()
        const sat = week[6]
        const satYear = sat.date.getFullYear()
        const satMonth = sat.date.getMonth()
        if (sunMonth === satMonth) {
          return `${sunYear}.${sunMonth + 1}.${sun.value}-${sat.value}`
        }
        if (sunYear === satYear) {
          return `${sunYear}.${sunMonth + 1}.${sun.value}-${satMonth + 1}.${sat.value}`
        }
        return `${sunYear}.${sunMonth + 1}.${sun.value}-${satYear}.${satMonth + 1}.${sat.value}`
      }
      const year = this.current.getFullYear()
      const month = this.current.getMonth()
      return `${year}.${month + 1}`
    },
    minRange () {
      const minDate = toZeroPoint(toDate(this.current))
      minDate.setDate(1)
      return minDate
    },
    maxRange () {
      const maxDate = toZeroPoint(toDate(this.current))
      maxDate.setDate(1)
      maxDate.setMonth(maxDate.getMonth() + 1)
      return maxDate
    },
    canToPrevious () {
      if (!this.current) {
        return false
      }
      if (this.mode === 0) {
        return this.weeks[this.weekIndex][0].date > this.minDate
      }
      return this.minRange > this.minDate
    },
    canToNext () {
      if (!this.current) {
        return false
      }
      if (!this.maxDate) {
        return true
      }
      if (this.mode === 0) {
        return this.weeks[this.weekIndex][6].date < this.maxDate
      }
      return this.maxRange <= this.maxDate
    },
    canToToday () {
      if (!this.current) {
        return false
      }
      const today = toZeroPoint(new Date())
      if (today < this.minDate || this.maxDate && today >= this.maxDate) {
        return false
      }
      if (this.mode === 0) {
        return !(this.weeks[this.weekIndex][0].date <= today && this.weeks[this.weekIndex][6].date >= today)
      }
      return today < this.minRange || today >= this.maxRange
    }
  },
  created () {
    this.init()
  },
  methods: {
    createEventProxy (event) {
      return {
        key: Math.random().toString(16).slice(2),
        origin: event
      }
    },
    init () {
      const events = this.events
      const length = events.length
      const today = new Date()
      let minDate = null
      let maxDate = null
      if (this.editable) {
        minDate = length ? pickMin(toDate(events[0].origin.start), today) : today
      } else if (length) {
        minDate = toDate(events[0].origin.start)
        for (let i = length - 1; i >= 0; i--) {
          const until = events[length - 1].origin.until
          if (!until) {
            maxDate = null
            break
          }
          if (!maxDate || maxDate < until) {
            maxDate = until
          }
        }
        maxDate = toDate(maxDate)
      } else {
        minDate = today
        maxDate = today
      }
      this.minDate = toZeroPoint(minDate)
      this.maxDate = maxDate
      this.toDay(maxDate ? isIn(today, minDate, maxDate) ? today : minDate : pickMax(minDate, today))
    },
    calculate () {
      const date = toDate(this.current)
      date.setDate(1)
      const today = toZeroPoint(new Date())
      const firstDayWeek = date.getDay()
      const prevMonthDays = this.getPrevMonthDays(date, firstDayWeek === 0 ? 7 : firstDayWeek, today)
      const monthDays = this.getMonthDays(date, today)
      const diff = 42 - prevMonthDays.length - monthDays.length
      const nextMonthDays = this.getNextMonthDays(date, diff, today)
      const weeks = []
      const days = [...prevMonthDays, ...monthDays, ...nextMonthDays]

      let row
      for (let i = 0; i < days.length; i++) {
        if (i % 7 === 0) {
          row = weeks.length
          weeks[row] = []
        }
        weeks[row][i % 7] = days[i]
      }
      this.weeks = weeks

      this.weekIndexStart = Math.floor(prevMonthDays.length / 7)
      this.weekIndexEnd = 6 - Math.floor(diff / 7)
    },
    getMonthDays (date, reference) {
      const year = date.getFullYear()
      const month = date.getMonth()
      const dateArr = []
      const lastDay = new Date(year, month + 1, 0).getDate()

      for (let i = 1; i <= lastDay; i++) {
        const temp = new Date(year, month, i)
        dateArr.push(this.createDayItem(temp, temp < reference))
      }
      return dateArr
    },
    getPrevMonthDays (date, amount, reference) {
      const dateArr = []

      if (amount) {
        date = toDate(date)
        date.setDate(0)
        const year = date.getFullYear()
        const month = date.getMonth()
        const lastDay = date.getDate()
        for (let i = amount; i > 0; i--) {
          const temp = new Date(year, month, lastDay - i + 1)
          dateArr.push(this.createDayItem(temp, temp < reference, true))
        }
      }

      return dateArr
    },
    getNextMonthDays (date, amount, reference) {
      const dateArr = []

      if (amount) {
        const year = date.getFullYear()
        const month = date.getMonth()
        for (let i = 0; i < amount; i++) {
          const temp = new Date(year, month + 1, i + 1)
          dateArr.push(this.createDayItem(temp, temp < reference, true))
        }
      }

      return dateArr
    },
    createDayItem (date, invalid, disabled) {
      return {
        date,
        value: date.getDate(),
        status: this.getStatus(invalid, disabled, this.editable),
        events: this.getEventsByDay(this.events, date)
      }
    },
    getStatus (invalid, disabled, editable) {
      if (invalid) {
        return 'invalid'
      }
      if (disabled) {
        return 'disabled'
      }
      return editable ? 'enabled' : 'normal'
    },
    getEventsByDay (events, date) {
      const arr = []
      if (events.length) {
        const minDate = date
        const maxDate = toDate(date.getTime() + ONE_DAY)
        const week = minDate.getDay()
        for (let i = 0; i < events.length; i++) {
          const { freq, start, until } = events[i].origin
          if (until && toDate(until) <= minDate) {
            continue
          }
          if (toDate(start) >= maxDate) {
            break
          }
          if (freq === EventFreq.WEEKLY) {
            const event = events[i].origin
            if (event.byDay.includes(week) || isOverDay(event) && event.byDay.includes(week === 0 ? 6 : week - 1)) {
              arr.push(events[i])
            }
          } else {
            arr.push(events[i])
          }
        }
      }
      return arr
    },
    toDay (date) {
      date = toZeroPoint(toDate(date))
      if (date >= this.minDate && (!this.maxDate || date < this.maxDate)) {
        this.current = date
        this.calculate()
        let weekIndex = this.weekIndexStart
        for (let i = this.weekIndexStart, j = this.weekIndexEnd; i <= j; i++) {
          const start = this.weeks[i][0].date
          const end = this.weeks[i][6].date
          if (start <= date && date <= end) {
            weekIndex = i
            break
          }
        }
        this.weekIndex = weekIndex
      }
    },
    onToday () {
      this.toDay(new Date())
    },
    onPrevious () {
      if (this.canToPrevious) {
        switch (this.mode) {
          case 0:
            this.offsetWeek(-1)
            break
          case 1:
            this.offsetMonth(-1)
            break
          default:
            break
        }
      }
    },
    onNext () {
      if (this.canToNext) {
        switch (this.mode) {
          case 0:
            this.offsetWeek(1)
            break
          case 1:
            this.offsetMonth(1)
            break
          default:
            break
        }
      }
    },
    offsetWeek (offset) {
      const next = this.weekIndex + offset
      if (next < this.weekIndexStart || next > this.weekIndexEnd) {
        const firstDate = this.weeks[this.weekIndex][0].date
        firstDate.setDate(firstDate.getDate() + 7 * offset)
        this.toDay(firstDate)
      } else {
        this.weekIndex += offset
      }
    },
    offsetMonth (offset) {
      const next = toDate(this.current)
      next.setDate(1)
      next.setMonth(next.getMonth() + offset)
      this.toDay(pickMax(this.minDate, next))
    },
    onChangeToMonth () {
      this.mode = 1
    },
    onChangeToWeek () {
      this.mode = 0
    }
  }
}
