/**
 * 将日期相关的帮助方法抽离到jslib
 */
import Lunar from 'chinese-lunar'
import getHoliday from './chinessHoliday'

export interface DateDesc {
  time: Date
  id: number
  value: number
  isPass?: boolean
  isFuture?: boolean
  isWeekDay?: boolean
  lunar?: string
  holiday?: string
}

export type DateDescList = DateDesc[]

interface GenDatesOptions {
  lunar: boolean
  holiday?: boolean
}

export const ChineseWeekDay = ['日', '一', '二', '三', '四', '五', '六']

export const Today = new Date()
export const ONE_WEEK = 7 * 24 * 3600 * 1000

const monthCache = {}
const defaultOptions = {
  lunar: true,
  holiday: true,
}
export function genDates(
  date: Date,
  option: GenDatesOptions = defaultOptions,
): DateDesc[] {
  // 缓存
  const cacheKey = `${date.getFullYear()}${date.getMonth()}`
  if (cacheKey in monthCache) {
    return monthCache[cacheKey]
  }

  let arr: DateDesc[] = []
  let time = new Date(date) // copy
  // 获取当月的第一天
  time.setMonth(time.getMonth(), 1)
  // 当月第一天的星期数
  let curFirstDay = time.getDay()
  /** 获取上个月的最后几天 **/
  // 更新为上个月的最后一天
  time.setDate(0)
  let lastDayCount = time.getDate()
  let tempYear = time.getFullYear()
  let tempMonth = time.getMonth()
  for (let i = curFirstDay; i > 0; i--) {
    let date = lastDayCount - i + 1
    let tempTime = new Date(tempYear, tempMonth, date)
    tempTime = startOfDate(tempTime)
    arr.push({
      time: tempTime,
      id: tempTime.getTime(),
      value: date,
      isPass: true,
      isWeekDay: isWeekDay(tempTime),
      lunar: option.lunar ? Lunar.solarToLunar(tempTime, 'D') : undefined,
      holiday: option.holiday ? getHoliday(tempTime) : undefined,
    })
  }
  /** 当月的日期数据 **/
  // 移动到获取当月的最后一天
  time.setMonth(time.getMonth() + 2, 0)
  // 当月的天数
  const curDayCount = time.getDate()
  tempYear = time.getFullYear()
  tempMonth = time.getMonth()
  for (let i = 1; i <= curDayCount; i++) {
    let tempTime = new Date(tempYear, tempMonth, i)
    tempTime = startOfDate(tempTime)
    arr.push({
      time: tempTime,
      id: tempTime.getTime(),
      value: i,
      isWeekDay: isWeekDay(tempTime),
      lunar: option.lunar ? Lunar.solarToLunar(tempTime, 'D') : undefined,
      holiday: option.holiday ? getHoliday(tempTime) : undefined,
    })
  }
  /** 下个月的前几天 **/
  let count = arr.length <= 35 ? 35 : 42
  let j = 1
  let l = count - arr.length + 1
  time.setMonth(tempMonth + 1, 1)
  tempYear = time.getFullYear()
  tempMonth = time.getMonth()
  while (j < l) {
    let tempTime = new Date(tempYear, tempMonth, j)
    tempTime = startOfDate(tempTime)
    arr.push({
      time: tempTime,
      id: tempTime.getTime(),
      value: j,
      isFuture: true,
      isWeekDay: isWeekDay(tempTime),
      lunar: option.lunar ? Lunar.solarToLunar(tempTime, 'D') : undefined,
      holiday: option.holiday ? getHoliday(tempTime) : undefined,
    })
    j++
  }
  // freeze object
  return (monthCache[cacheKey] = arr)
}

export function isWeekDay(date: Date) {
  const day = date.getDay()
  return day != 0 && day != 6
}

export function isSameYear(a: Date, b: Date) {
  return a.getFullYear() === b.getFullYear()
}

export function isSameMonth(a: Date, b: Date) {
  return isSameYear(a, b) && a.getMonth() === b.getMonth()
}

export function isSameDay(a: Date, b: Date) {
  return (
    a.getFullYear() === b.getFullYear() &&
    a.getMonth() === b.getMonth() &&
    a.getDate() === b.getDate()
  )
}

export function isSameHour(a: Date, b: Date) {
  return isSameDay(a, b) && a.getHours() === b.getHours()
}

export function isSameMinute(a: Date, b: Date) {
  return isSameHour(a, b) && a.getMinutes() === b.getMinutes()
}

/**
 * 判断是否是闰年
 * @param year
 */
export function isLeap(year: number) {
  return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0
}

export function isToday(date: Date) {
  return isSameDay(Today, date)
}

export function inRange(start: Date, end: Date, value: Date) {
  if (end.getTime() < start.getTime()) {
    const temp = start
    start = end
    end = temp
  }
  return value >= start && value <= end
}

export function inRangeIgnoreTime(start: Date, end: Date, value: Date) {
  if (end.getTime() < start.getTime()) {
    const temp = start
    start = end
    end = temp
  }

  return (
    isSameDay(start, value) ||
    isSameDay(end, value) ||
    inRange(start, end, value)
  )
}

export function inMonth(month: Date, value: Date) {
  const start = new Date(month)
  start.setDate(1)
  start.setHours(0, 0, 0, 0)
  const end = new Date(start)
  end.setMonth(end.getMonth() + 1)
  end.setDate(0)
  end.setHours(23, 59, 59, 999)
  return inRange(start, end, value)
}

export function yesterday(date: Date) {
  const copy = new Date(date)
  copy.setDate(copy.getDate() - 1)
  return copy
}

export function tomorrow(date: Date) {
  const copy = new Date(date)
  copy.setDate(copy.getDate() + 1)
  return copy
}

export function startOfDate(date: Date) {
  const copy = new Date(date)
  copy.setHours(0, 0, 0, 0)
  return copy
}

export function endOfDate(date: Date) {
  const copy = new Date(date)
  copy.setHours(23, 59, 59, 999)
  return copy
}

export function midOfDate(date: Date) {
  const copy = new Date(date)
  copy.setHours(12, 0, 0, 0)
  return copy
}

export function outOfCurrentYear(date: Date) {
  return date.getFullYear() !== Today.getFullYear()
}

export function getLastDateOfMonth(date: Date) {
  const copy = new Date(date)
  copy.setMonth(copy.getMonth() + 1)
  copy.setDate(0)
  return copy.getDate()
}

export function afterHour(date: Date, hour: number = 1) {
  const copy = new Date(date)
  copy.setHours(copy.getHours() + hour)
  return copy
}

export function afterDay(date: Date, day: number = 1) {
  const copy = new Date(date)
  copy.setDate(copy.getDate() + day)
  return copy
}

export function afterMonth(date: Date, month: number = 1) {
  const copy = new Date(date)
  copy.setDate(1)
  copy.setMonth(copy.getMonth() + month)
  const lastDate = getLastDateOfMonth(copy)
  if (date.getDate() > lastDate) {
    copy.setDate(lastDate)
  } else {
    copy.setDate(date.getDate())
  }

  return copy
}

export function afterYear(date: Date, year: number = 1) {
  return afterMonth(date, year * 12)
}

export function monthDiff(a: Date, b: Date) {
  const yearDiff = a.getFullYear() - b.getFullYear()
  const monthDiff = a.getMonth() - b.getMonth()

  return yearDiff * 12 + monthDiff
}

export function copyDate(from: Date, to: Date) {
  to.setFullYear(from.getFullYear())
  to.setMonth(from.getMonth())
  to.setDate(from.getDate())
  return to
}

export function copyTime(from: Date, to: Date) {
  to.setHours(
    from.getHours(),
    from.getMinutes(),
    from.getSeconds(),
    from.getMilliseconds(),
  )
  return to
}

export function roundHour(date: Date) {
  const copy = new Date(date)
  const min = copy.getMinutes()
  copy.setMinutes(0)
  copy.setSeconds(0)
  copy.setMilliseconds(0)
  if (min > 0) {
    copy.setHours(copy.getHours() + 1)
  }
  return copy
}
