import { computed, nextTick, reactive, ref, watch } from 'vue'
import dayjs from '../../../../libs/dayjs'
import { CHANGE_EVENT, UPDATE_MODEL_EVENT } from '../../../../constants'
import { debugWarn, isString } from '../../../../utils'
import { solar2lunar } from '../../libs/lunar-calendar'
import { useCalendarSelector } from './use-calendar-selector'

import type { SetupContext } from 'vue'
import type { Dayjs } from '../../../../libs/dayjs'
import type {
  CalendarEmits,
  CalendarModelValueType,
  CalendarProps,
} from '../calendar'
import type {
  CalendarData,
  CalendarItem,
  CalendarItemDateStatus,
  CalendarMonthData,
  CalendarRangeSelectData,
  CalendarSelectDataMap,
  DateData,
} from '../types'

// 默认的日期格式
const DEFAULT_DATE_FORMAT = 'YYYY/MM/DD'

// 格式化日期数据，将-替换为/
const formatDate = <T extends CalendarModelValueType>(date: T): T => {
  if (!date || !date.length) return date
  if (isString(date)) {
    return date.replace(/-/g, '/') as T
  }
  return date.map((item) => item.replace(/-/g, '/')) as T
}

// 填充0
const fillDateZero = (date: number) => String(date).padStart(2, '0')

// 根据年月日生成日期字符串
const generateDateStr = (date: DateData) =>
  `${date.year}/${fillDateZero(date.month)}/${fillDateZero(date.date)}`

export const useCalendar = (
  props: CalendarProps,
  emits: SetupContext<CalendarEmits>['emit']
) => {
  // 重新加载月份swiper
  const reloadMonthSwiper = ref<boolean>(false)
  // 日历的数据
  const calendarData = ref<CalendarData>([])

  // 星期提示文字
  const weekText = ref<string[]>(['日', '一', '二', '三', '四', '五', '六'])

  // 最小年月日
  const minDate = reactive<DateData>({
    year: 0,
    month: 0,
    date: 0,
  })
  // 最大年月日
  const maxDate = reactive<DateData>({
    year: 0,
    month: 0,
    date: 0,
  })

  // 当前年月日
  const currentDate = reactive<DateData>({
    year: 0,
    month: 0,
    date: 0,
  })

  // 当前选中的年月日
  const currentSelectedDate = reactive<DateData>({
    year: 0,
    month: 0,
    date: 0,
  })

  // 日期范围内数据
  const rangeDate = reactive<CalendarRangeSelectData>({})

  // 更新modelValue
  // 标记是否需要重新渲染日历
  let needRenderCalendar = false
  const updateModelValue = (changeEmit = true) => {
    const { modelValue, mode } = props
    const formatModelValue = formatDate(modelValue)
    let value: CalendarModelValueType = ''
    // 根据不同的模式，返回不同的值
    switch (mode) {
      case 'date':
        // 如果当前选中的日期不为空，则先将之前选中日期的选中状态修改为normal
        if (modelValue) {
          const dateDayjs = dayjs(
            formatModelValue as string,
            DEFAULT_DATE_FORMAT
          )
          updateDateStatus(dateDayjs, 'normal')
        }
        // 设置当前选中日期为激活状态
        updateDateStatus(currentSelectedDate, 'active')
        value = generateDateStr(currentSelectedDate)
        break
      case 'multi':
        // 判断当前选中的日期是否已经存在于modelValue中，如果存在，则将其从modelValue中移除
        // eslint-disable-next-line no-case-declarations
        const activeIndex = (formatModelValue as string[]).indexOf(
          generateDateStr(currentSelectedDate)
        )
        if (formatModelValue.length && activeIndex !== -1) {
          updateDateStatus(currentSelectedDate, 'normal')
          ;(formatModelValue as string[]).splice(activeIndex, 1)
          value = [...(formatModelValue as string[])]
        } else {
          updateDateStatus(currentSelectedDate, 'active')
          value = [
            ...(formatModelValue as string[]),
            generateDateStr(currentSelectedDate),
          ]
        }
        break
      case 'range':
        // 如果已选的值为空或者长度为2，则将当前选中的日期设置为开始日期
        if (
          !formatModelValue ||
          formatModelValue.length === 0 ||
          formatModelValue.length === 2
        ) {
          if (formatModelValue.length === 2) {
            // 清空原来设置的时间
            const startDateDayjs = dayjs(
              (formatModelValue as string[])[0],
              DEFAULT_DATE_FORMAT
            )
            const endDateDayjs = dayjs(
              (formatModelValue as string[])[1],
              DEFAULT_DATE_FORMAT
            )
            updateDateStatus(startDateDayjs, 'normal')
            updateDateStatus(endDateDayjs, 'normal')
          }
          value = [generateDateStr(currentSelectedDate)]
        } else {
          // 判断当前选中的日期是否比开始日期还要着，如果是则重新设置开始日期
          const currentDate = generateDateStr(currentSelectedDate)
          if (rangeDate.start) {
            if (rangeDate.start.isAfter(generateDateStr(currentSelectedDate))) {
              value = [currentDate]
            } else {
              // 将选择后的日添加为结束日期
              value = [
                ...(formatModelValue as string[]),
                generateDateStr(currentSelectedDate),
              ]
            }
          }
        }
        needRenderCalendar = true
        break
    }

    emits(UPDATE_MODEL_EVENT, value)
    if (changeEmit) {
      nextTick(() => {
        emits(CHANGE_EVENT, value)
      })
    }
  }

  // 当前月份在中数据中的索引
  const currentMonthIndex = computed(() => {
    return calendarData.value.findIndex(
      (item) => item.month === currentSelectedDate.month
    )
  })
  // 当前月份中日期数据的长度
  const currentMonthDateLength = computed(() => {
    return calendarData.value[currentMonthIndex.value]?.data?.length || 0
  })

  // 处理容器信息
  const { calendarId, dateContainerHeight } = useCalendarSelector(
    currentMonthDateLength,
    props.mode
  )

  // 保存当前设置的日期信息
  const activeDateValueMap = new Map<number, CalendarSelectDataMap>()

  // 生成日历数据
  const generateCalendarData = () => {
    const data: CalendarData = []
    // 填充日期数据
    const _fillDateData = (
      month: number,
      minDisabledDate = 0,
      maxDisabledDate = 0
    ) => {
      const monthData: CalendarMonthData = {
        month,
        data: [],
      }

      // 获取对应年份和月份
      const activeDates = activeDateValueMap
        .get(currentSelectedDate.year)
        ?.get(month)

      // 获取当前月份的天数
      const days = new Date(currentSelectedDate.year, month, 0).getDate()
      // 获取当前月份的第一天是星期几
      const firstDayWeek = new Date(
        `${currentSelectedDate.year}/${month}/1`
      ).getDay()
      // 填充空白数据
      for (let i = 0; i < firstDayWeek; i++) {
        monthData.data.push({
          date: 0,
          status: 'disabled',
        })
      }
      // 填充日期数据
      for (let i = 1; i <= days; i++) {
        let status: CalendarItemDateStatus =
          i < minDisabledDate || (maxDisabledDate && i > maxDisabledDate)
            ? 'disabled'
            : 'normal'
        if (activeDates?.includes(i)) status = 'active'
        let desc = ''
        if (props.showLunar) {
          const lunarValue = solar2lunar(currentSelectedDate.year, month, i)
          if (lunarValue !== -1) {
            desc =
              lunarValue.IDayCn === '初一'
                ? lunarValue.IMonthCn
                : lunarValue.IDayCn
          }
        }

        // 设置日期范围数据
        if (props.mode === 'range') {
          const { start, end } = rangeDate
          if (
            start &&
            start.isSame(`${currentSelectedDate.year}/${month}/${i}`)
          ) {
            status = 'active'
            desc = props.rangeStartDesc
          }
          if (end && end.isSame(`${currentSelectedDate.year}/${month}/${i}`)) {
            status = 'active'
            desc = props.rangeEndDesc
          }
          if (start && end) {
            // 判断是否在范围内
            const currentDateDayjs = dayjs(
              `${currentSelectedDate.year}/${month}/${i}`,
              DEFAULT_DATE_FORMAT
            )
            if (
              currentDateDayjs.isAfter(start) &&
              currentDateDayjs.isBefore(end)
            ) {
              status = 'range'
            }
          }
        }

        monthData.data.push({
          date: i,
          status,
          desc,
        })
      }

      return monthData
    }

    // 填充月份数据
    let minMonth = 1
    let maxMonth = 12

    // 如果当前选中的年份等于最小年份，那么最小月份就是最小年份的月份
    if (currentSelectedDate.year === minDate.year) {
      minMonth = minDate.month
    }
    if (currentSelectedDate.year === maxDate.year) {
      maxMonth = maxDate.month
    }

    if (minMonth === 0 || maxMonth === 0) return

    for (let i = minMonth; i <= maxMonth; i++) {
      let minDisabledDate = 0
      let maxDisabledDate = 0
      // 如果当前选中的年份等于最小年份，那么最小月份就是最小年份的月份
      if (currentSelectedDate.year === minDate.year && i === minMonth) {
        minDisabledDate = minDate.date
        maxDisabledDate = 0
      }
      if (currentSelectedDate.year === maxDate.year && i === maxMonth) {
        minDisabledDate = 0
        maxDisabledDate = maxDate.date
      }
      data.push(_fillDateData(i, minDisabledDate, maxDisabledDate))
    }

    calendarData.value = data
  }

  watch(
    () => props.modelValue,
    (val: CalendarModelValueType) => {
      if (val || val.length) {
        const { mode } = props
        let modelValue: string[] = []
        if (mode === 'date') modelValue = [val as string]
        else modelValue = val as string[]

        activeDateValueMap.clear()

        // 遍历获取对应已设置的日期信息
        modelValue.forEach((item) => {
          const dateDayjs = dayjs(formatDate(item), DEFAULT_DATE_FORMAT)
          const year = dateDayjs.year()
          const month = dateDayjs.month() + 1
          const date = dateDayjs.date()
          // 设置当前激活的年份和月份
          if (currentSelectedDate.year === 0) {
            currentSelectedDate.year = year
            currentSelectedDate.month = month
          }
          if (activeDateValueMap.has(year)) {
            let monthDates = activeDateValueMap.get(year)?.get(month)
            if (!monthDates) monthDates = [date]
            else monthDates.push(date)
            activeDateValueMap.get(year)?.set(month, monthDates)
          } else {
            activeDateValueMap.set(year, new Map([[month, [date]]]))
          }
        })

        // 如果是选择日期范围，则设置开始和结束时间
        if (mode === 'range') {
          rangeDate.start = undefined
          rangeDate.end = undefined
          if (modelValue?.[0]) {
            // 开始时间
            rangeDate.start = dayjs(modelValue[0], DEFAULT_DATE_FORMAT)
          }
          if (modelValue?.[1]) {
            // 结束时间
            rangeDate.end = dayjs(modelValue[1], DEFAULT_DATE_FORMAT)
          }
        }

        if (needRenderCalendar) {
          needRenderCalendar = false
          nextTick(() => {
            generateCalendarData()
          })
        }
      }
    },
    {
      immediate: true,
      deep: true,
    }
  )

  // 根据传递的日期，设置当前日期的状态
  const updateDateStatus = (
    dateData: DateData | Dayjs,
    status: CalendarItemDateStatus
  ) => {
    let month = 0
    let date = 0
    if (dateData.toString() === '[object Object]') {
      month = (dateData as DateData).month
      date = (dateData as DateData).date
    } else {
      month = (dateData as Dayjs).month() + 1
      date = (dateData as Dayjs).date()
    }
    const monthIndex = calendarData.value.findIndex(
      (item) => item.month === month
    )
    if (monthIndex === -1) return
    const dateIndex = calendarData.value[monthIndex].data.findIndex(
      (item) => item.date === date
    )
    if (dateIndex === -1) return
    calendarData.value[monthIndex].data[dateIndex].status = status
  }

  // 初始化最小、最大年月日
  const initDateData = () => {
    let { minDate: _minDate, maxDate: _maxDate, modelValue, mode } = props

    // 当前时间的 dayjs 对象
    const currentDayjs = dayjs()
    currentDate.year = currentDayjs.year()
    currentDate.month = currentDayjs.month() + 1
    currentDate.date = currentDayjs.date()

    // 如果没有设置最小值，那么最小值就是当前年月日
    if (!_minDate) {
      _minDate = currentDayjs.format(DEFAULT_DATE_FORMAT)
    } else {
      _minDate = formatDate(_minDate)
    }
    const minDateDayjs = dayjs(_minDate, DEFAULT_DATE_FORMAT)
    minDate.year = minDateDayjs.year()
    minDate.month = minDateDayjs.month() + 1
    minDate.date = minDateDayjs.date()

    // 如果没有设置最大值，那么最大值就是当前年份最后一个月的最后一天
    if (!_maxDate) {
      _maxDate = currentDayjs.endOf('year').format(DEFAULT_DATE_FORMAT)
    } else {
      _maxDate = formatDate(_maxDate)
    }
    const maxDateDayjs = dayjs(_maxDate, DEFAULT_DATE_FORMAT)
    maxDate.year = maxDateDayjs.year()
    maxDate.month = maxDateDayjs.month() + 1
    maxDate.date = maxDateDayjs.date()

    let noGenerateAfterInit = false
    // 如果没有设置默认值，那么默认值就是当前年月
    if (!modelValue || !modelValue?.length) {
      currentSelectedDate.year = Math.max(currentDate.year, minDate.year)
      currentSelectedDate.month =
        currentDate.year === minDate.year
          ? Math.max(currentDate.month, minDate.month)
          : currentDate.month

      // 如果当前是单选日期模式，那么默认选中当前日期
      if (
        mode === 'date' &&
        currentSelectedDate.year === currentDate.year &&
        currentSelectedDate.month === currentDate.month
      ) {
        currentSelectedDate.date = currentDate.date
        emits(UPDATE_MODEL_EVENT, generateDateStr(currentSelectedDate))
      }
    } else {
      let initDefaultDate = ''
      switch (mode) {
        case 'date':
          initDefaultDate = formatDate(modelValue as string)
          // 判断设置的默认值是否在最小、最大年月日范围内，如果不在，那么默认值就是最小、最大年月日
          if (minDateDayjs.isAfter(initDefaultDate)) {
            initDefaultDate = minDateDayjs.format(DEFAULT_DATE_FORMAT)
            emits(UPDATE_MODEL_EVENT, initDefaultDate)
          }
          if (maxDateDayjs.isBefore(initDefaultDate)) {
            initDefaultDate = maxDateDayjs.format(DEFAULT_DATE_FORMAT)
            emits(UPDATE_MODEL_EVENT, initDefaultDate)
          }
          break
        case 'multi':
          // 筛选出在最小、最大年月日范围内的日期
          // eslint-disable-next-line no-case-declarations
          const multiDefaultModelValue = (modelValue as string[]).filter(
            (date) => {
              const dateDayjs = dayjs(date)
              return (
                (dateDayjs.isAfter(minDateDayjs) &&
                  dateDayjs.isBefore(maxDateDayjs)) ||
                dateDayjs.isSame(minDateDayjs) ||
                dateDayjs.isSame(maxDateDayjs)
              )
            }
          )
          if (
            multiDefaultModelValue.length !== (modelValue as string[]).length
          ) {
            emits(UPDATE_MODEL_EVENT, multiDefaultModelValue)
          }
          initDefaultDate = multiDefaultModelValue[0]

          break
        case 'range':
          if (modelValue.length !== 2) {
            debugWarn('TnCalendar', '在 range 模式下 modelValue 长度必须为 2')
            return
          }
          // eslint-disable-next-line no-case-declarations
          const rangeMinDateDayjs = dayjs((modelValue as string[])[0])
          // eslint-disable-next-line no-case-declarations
          const rangeMaxDateDayjs = dayjs((modelValue as string[])[1])
          // 判断是否超过最大范围
          if (
            rangeMinDateDayjs.isAfter(maxDateDayjs) ||
            rangeMaxDateDayjs.isBefore(minDateDayjs)
          ) {
            debugWarn('TnCalendar', '在 range 模式下 modelValue 超过最大范围')
            return
          }
          // eslint-disable-next-line no-case-declarations
          const rangeDefaultModelValue = modelValue as string[]
          if (rangeMinDateDayjs.isBefore(minDateDayjs)) {
            rangeDefaultModelValue[0] = minDateDayjs.format(DEFAULT_DATE_FORMAT)
            initDefaultDate = rangeDefaultModelValue[0]
          }
          if (rangeMaxDateDayjs.isAfter(maxDateDayjs)) {
            rangeDefaultModelValue[1] = maxDateDayjs.format(DEFAULT_DATE_FORMAT)
          }
          emits(UPDATE_MODEL_EVENT, rangeDefaultModelValue)
          noGenerateAfterInit = true

          break
      }
      if (initDefaultDate) {
        const initDefaultDateDayjs = dayjs(initDefaultDate, DEFAULT_DATE_FORMAT)
        currentSelectedDate.year = initDefaultDateDayjs.year()
        currentSelectedDate.month = initDefaultDateDayjs.month() + 1
        currentSelectedDate.date = initDefaultDateDayjs.date()
      }
    }
    if (!noGenerateAfterInit || calendarData.value.length === 0) {
      nextTick(() => {
        // 生成日历数据
        generateCalendarData()
      })
    }
  }
  initDateData()

  // 如果修改了最小、最大年月日，那么重新初始化最小、最大年月日
  watch(
    () => [props.minDate, props.maxDate],
    () => {
      initDateData()
    }
  )

  // dateItem点击事件
  const dateItemClickEvent = (date: CalendarItem) => {
    if (
      (date.status === 'active' && props.mode === 'date') ||
      date.status === 'disabled' ||
      date.date === 0
    )
      return
    currentSelectedDate.date = date.date
    updateModelValue()
  }

  // 滑动切换月份事件
  const swiperSwitchMonthEvent = (event: any) => {
    const { current } = event.detail
    currentSelectedDate.month = calendarData.value[current].month
    clearSwiperAnimationFlagTimer = setTimeout(() => {
      swiperSwitchAnimationFinish = true
      if (clearSwiperAnimationFlagTimer) {
        clearTimeout(clearSwiperAnimationFlagTimer)
        clearSwiperAnimationFlagTimer = null
      }
    }, 300)
    emits('change-month', currentSelectedDate.month)
  }
  // 月份切换动画结束
  let swiperSwitchAnimationFinish = true
  let clearSwiperAnimationFlagTimer: ReturnType<typeof setTimeout> | null = null
  const swiperSwitchMonthAnimationFinishEvent = () => {
    swiperSwitchAnimationFinish = true
    if (clearSwiperAnimationFlagTimer) {
      clearTimeout(clearSwiperAnimationFlagTimer)
      clearSwiperAnimationFlagTimer = null
    }
  }

  // 切换月份
  const switchMonth = (type: 'prev' | 'next') => {
    if (!props.allowChangeMonth || !swiperSwitchAnimationFinish) return
    swiperSwitchAnimationFinish = false
    if (type === 'prev') {
      if (
        (currentSelectedDate.year === minDate.year &&
          currentSelectedDate.month === minDate.month) ||
        currentSelectedDate.month === 1
      )
        return
      currentSelectedDate.month--
    } else {
      if (
        (currentSelectedDate.year === maxDate.year &&
          currentSelectedDate.month === maxDate.month) ||
        currentSelectedDate.month === 12
      )
        return
      currentSelectedDate.month++
    }
  }

  // 切换年份
  const switchYear = (type: 'prev' | 'next') => {
    if (!props.allowChangeYear) return
    if (type === 'prev') {
      if (currentSelectedDate.year === minDate.year) return
      currentSelectedDate.year--
      currentSelectedDate.month = 12
      currentSelectedDate.date = 31
    } else {
      if (currentSelectedDate.year === maxDate.year) return
      currentSelectedDate.year++
      currentSelectedDate.month = 1
      currentSelectedDate.date = 1
    }

    emits('change-year', currentSelectedDate.year)

    reloadMonthSwiper.value = true
    setTimeout(() => {
      reloadMonthSwiper.value = false
    }, 0)

    // 重新生产日历数据
    generateCalendarData()
  }

  return {
    reloadMonthSwiper,
    calendarId,
    weekText,
    calendarData,
    currentMonthIndex,
    dateContainerHeight,
    minDate,
    maxDate,
    currentSelectedDate,
    swiperSwitchMonthEvent,
    swiperSwitchMonthAnimationFinishEvent,
    dateItemClickEvent,
    switchMonth,
    switchYear,
  }
}
