import { EventFreq } from '@/constant'

export const ONE_DAY = 86400000

export function isMoreThanOneWeek (timestamp) {
  return timestamp >= 604800000
}

export function isOverDay ({ startTime, endTime }) {
  if (startTime === endTime) {
    return startTime !== '00:00:00'
  }
  return startTime > endTime
}

export function getNearestHitDate (event, minDate, maxDate) {
  switch (event.freq) {
    case EventFreq.ONCE:
      return getNearestHitDateForOnce(event, minDate, maxDate)
    case EventFreq.WEEKLY:
      return getNearestHitDateForWeekly(event, minDate, maxDate)
    default:
      return null
  }
}

function getNearestHitDateForOnce (event, minDate, maxDate) {
  minDate = pickMax(toDate(event.start), toDate(minDate))
  maxDate = pickMin(toDate(event.until), toDate(maxDate))
  console.log('getNearestHitDateForOnce', minDate, maxDate)
  return maxDate && maxDate <= minDate ? null : minDate
}

function getNearestHitDateForWeekly (event, minDate, maxDate) {
  console.log('getNearestHitDateForWeekly', event)
  minDate = getNearestHitDateForOnce(event, minDate, maxDate)
  if (!minDate) {
    return null
  }
  if (isHitWeeklyByDate(event, minDate)) {
    return minDate
  }
  maxDate = pickMin(toDate(event.until), toDate(maxDate))
  const byDay = event.byDay
  const date = toDate(`${toDateStr(minDate)} ${event.startTime}`)
  if (date < minDate) {
    date.setDate(date.getDate() + 1)
  }
  while (canContinue(date, maxDate)) {
    if (byDay.includes(date.getDay())) {
      return date
    }
    date.setDate(date.getDate() + 1)
  }
  return null
}

function offsetWeek (week, offset) {
  return (week + offset + 7) % 7
}

export function toDate (val) {
  return val && new Date(typeof val === 'string' ? val.replace(/-/g, '/') : val)
}

export function pickMax (a, b) {
  return a >= b ? a : b
}

export function pickMin (a, b) {
  return a && b
    ? a >= b
      ? b
      : a
    : a || b
}

export function isHit (event, date) {
  console.log('isHit', event, date)
  if (!isIn(toDate(date), toDate(event.start), toDate(event.until))) {
    console.log('miss', 'out of range')
    return false
  }
  switch (event.freq) {
    case EventFreq.WEEKLY:
      return isHitWeeklyByDate(event, date)
    default:
      return true
  }
}

function isHitWeeklyByDate (event, date) {
  date = toDate(date)
  const byDay = event.byDay
  const week = date.getDay()
  if (!isOverDay(event)) {
    return byDay.includes(week) && isIn(toTimeStr(date), event.startTime, correctEndTime(event.endTime))
  }
  if (byDay.includes(week) && isIn(toTimeStr(date), event.startTime, '24:00:00')) {
    return true
  }
  if (byDay.includes(offsetWeek(week, -1)) && isIn(toTimeStr(date), '00:00:00', event.endTime)) {
    return true
  }
  return false
}

function canContinue (startDate, endDate) {
  return !endDate || startDate < endDate
}

export function isIn (target, start, end) {
  return target >= start && (!end || target < end)
}

export function toDateStr (date, offset = 0) {
  if (offset !== 0) {
    date = toDate(date)
    date.setDate(date.getDate() + offset)
  }
  return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`
}

export function toTimeStr (date) {
  return `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}:${date.getSeconds().toString().padStart(2, '0')}`
}

export function toZeroPoint (date) {
  return toDate(`${toDateStr(date)} 00:00:00`)
}

export function getStartDate (event, date) {
  switch (event.freq) {
    case EventFreq.WEEKLY:
      return getWeeklyStartDate(event, date)
    default:
      return new Date(event.start)
  }
}

function getWeeklyStartDate (event, date) {
  const time = toTimeStr(date)
  return time < event.startTime
    ? new Date(`${toDateStr(date, -1)} ${event.startTime}`)
    : new Date(`${toDateStr(date)} ${event.startTime}`)
}

export function getFinishDate (event, date) {
  switch (event.freq) {
    case EventFreq.WEEKLY:
      return getWeeklyFinishDate(event, date)
    default:
      return event.until && new Date(event.until)
  }
}

function getWeeklyFinishDate (event, date) {
  const time = toTimeStr(date)
  return time < event.startTime
    ? new Date(`${toDateStr(date)} ${event.endTime}`)
    : new Date(`${toDateStr(date, isOverDay(event) ? 1 : 0)} ${correctEndTime(event.endTime)}`)
}

export function getConflict (a, b) {
  console.log('compare', a, b)
  if (a.freq !== b.freq) {
    return a.freq === EventFreq.WEEKLY
      ? getNearestHitDateForWeekly(a, b.start, b.until)
      : getNearestHitDateForWeekly(b, a.start, a.until)
  }
  if (a.freq === EventFreq.WEEKLY) {
    return getConflictForWeeklyToWeekly(a, b)
  }
  return getNearestHitDateForOnce(a, b.start, b.until)
}

function getConflictForWeeklyToWeekly (a, b) {
  const hitDate = getNearestHitDateForOnce(a, b.start, b.until)
  if (hitDate) {
    const untilDate = pickMin(toDate(a.until), toDate(b.until))
    const isMore = !untilDate || isMoreThanOneWeek(untilDate - hitDate)
    const minDate = isMore ? toDate(`${toDateStr(hitDate)} 00:00:00`) : hitDate
    const maxDate = isMore ? toDate(`${toDateStr(hitDate, 7)} 00:00:00`) : untilDate
    console.log('isConflictForWeeklyToWeekly', minDate, maxDate)
    const aTimeSlices = getTimeSlicesInRange(a, minDate, maxDate)
    if (aTimeSlices[0].length) {
      console.log('isConflictForWeeklyToWeekly', a, aTimeSlices)
      const bTimeSlices = getTimeSlicesInRange(b, minDate, maxDate)
      if (bTimeSlices[0].length) {
        console.log('isConflictForWeeklyToWeekly', b, bTimeSlices)
        const fromTimeSlices = [...aTimeSlices[0], ...bTimeSlices[0]].sort(ascSort)
        const toTimeSlices = [...aTimeSlices[1], ...bTimeSlices[1]].sort(ascSort)
        return getConflictTimeSlice(fromTimeSlices, toTimeSlices)
      }
    }
  }
  console.log('miss')
  return null
}

function getConflictTimeSlice (fromTimeSlices, toTimeSlices) {
  let i = 0
  let j = 0
  let running = false
  while (i < fromTimeSlices.length && j < toTimeSlices.length) {
    if (fromTimeSlices[i] < toTimeSlices[j]) {
      if (running) {
        return fromTimeSlices[i]
      }
      running = true
      i += 1
    } else {
      running = false
      j += 1
    }
  }
  console.log('time slice miss')
  return null
}

function ascSort (a, b) {
  return a - b
}

function getTimeSlicesInRange (event, minDate, maxDate) {
  const { byDay, startTime } = event
  const endTime = correctEndTime(event.endTime)
  const minDateStr = toDateStr(minDate)
  const fromTimeSlices = []
  const toTimeSlices = []
  if (isHitOverDayTime(event, minDate)) {
    fromTimeSlices.push(toDate(minDate))
    toTimeSlices.push(pickMin(toDate(`${minDateStr} ${endTime}`), maxDate))
  }
  const referFromDate = toDate(`${minDateStr} ${startTime}`)
  if (referFromDate < maxDate) {
    const referToDate = toDate(`${toDateStr(minDate, isOverDay(event) ? 1 : 0)} ${endTime}`)
    while (canContinue(referFromDate, maxDate)) {
      if (byDay.includes(referFromDate.getDay())) {
        fromTimeSlices.push(toDate(pickMax(referFromDate, minDate)))
        toTimeSlices.push(toDate(pickMin(referToDate, maxDate)))
      }
      referFromDate.setDate(referFromDate.getDate() + 1)
      referToDate.setDate(referToDate.getDate() + 1)
    }
  }
  return [fromTimeSlices, toTimeSlices]
}

export function isHitOverDayTime (event, date) {
  if (!isOverDay(event)) {
    return null
  }
  date = toDate(date)
  const { byDay, endTime } = event
  return byDay.includes(offsetWeek(date.getDay(), -1)) && toTimeStr(date) < endTime
}

export function correctEndTime (endTime) {
  return endTime === '00:00:00' ? '24:00:00' : endTime
}

export function getEventDateDescription (event) {
  const { start, until } = event
  return until ? `${start} - ${until}` : `自${start}开始`
}

export function getEventExtraDescription (event) {
  const { freq, byDay, start, until, startTime, endTime, target } = event
  switch (freq) {
    case EventFreq.WEEKLY:
      return `每周${byDay.split(',').map(val => ['日', '一', '二', '三', '四', '五', '六'][val]).join('、')} ${startTime} - ${endTime}`
    default:
      return target?.duration
        ? `播放${(new Date(until) - new Date(start)) / target.duration / 1000}次`
        : ''
  }
}

export function getEventDescription (event) {
  const extra = getEventExtraDescription(event)
  if (extra) {
    return `${getEventDateDescription(event)} ${extra}`
  }
  return getEventDateDescription(event)
}
