<script setup lang="ts">
import {
  getYear,
  getMonth,
  startOfDay,
  globalDateFormat,
} from '@seed-next/date'

interface Props {
  date: Date | string | null
  minDate?: Date | null
  maxDate?: Date | null
  placeholder?: Date | string | null
}

const props = withDefaults(defineProps<Props>(), {
  minDate: null,
  maxDate: null,
  placeholder: null,
})

const emits = defineEmits(['update:date'])

// Composabled
const breakpoints = useResponsiveBreakpoints()

// Refs -----
const { t, locale } = useI18n()
const datePickerRef = ref<any>(null)
const resultDate = ref('')
const formatDate = ref('')
const monthList = ref<Array<number>>([])
const currentYear = ref(getYear(new Date()))
const currentMonth = ref(getMonth(new Date()))
const currentDecade = ref<{ start: number; end: number; year: number }>({
  start: 1970,
  end: Math.floor(currentYear.value / 10) * 10 + 9,
  year: currentYear.value,
})
const currentDecadeList = ref<Array<number> | null>(null)
// 상태값
const isMenuOpened = ref(false)
const isShowYearPicker = ref(false)
const isShowMonthPicker = ref(false)
const isDisabledYearNav = ref({ prev: false, next: false })
const isDisabledDecadeNav = ref({ prev: false, next: false })

// Computed
const formatMinDate = computed(() => {
  if (props.minDate !== null) {
    return globalDateFormat(props.minDate, locale.value)
  } else {
    return undefined
  }
})
const formatMaxDate = computed(() => {
  if (props.maxDate !== null) {
    return globalDateFormat(props.maxDate, locale.value)
  } else {
    return undefined
  }
})
const formatPlaceholder = computed(() => {
  if (props.placeholder !== null) {
    return props.placeholder instanceof Date
      ? (globalDateFormat(props.placeholder, locale.value) as string)
      : props.placeholder
  } else {
    return ''
  }
})

// Functions
/**
 * @description 날짜 Computed값 포맷 셋팅 함수입니다.
 */
const setFormatDate = () => {
  const toDay = startOfDay(new Date())
  resultDate.value =
    props.date === null ? toDay.toString() : props.date.toString()
  formatDate.value = globalDateFormat(resultDate.value, locale.value) as string

  const dateObj = props.date === null ? toDay : new Date(props.date)
  currentYear.value = getYear(dateObj)
  currentMonth.value = getMonth(dateObj)
  updateDecade(currentYear.value)
}

/**
 * @description 선택한 날짜로 업데이트하는 함수입니다.
 * @param _date<Date | null> 선택한 날짜
 */
const updateDate = (_date: Date | null) => {
  if (_date != null) {
    formatDate.value = globalDateFormat(_date, locale.value) as string
    currentYear.value = getYear(_date)
    currentMonth.value = getMonth(_date)
    emits('update:date', formatDate.value)
  }
}

/**
 * @description 월/년도가 변경되었을 때 업데이트하는 함수입니다.
 * @param newDate<object<{ year: number, month: number }>> 변경된 월/년도
 */
const changeMonthYear = (newDate: any) => {
  currentYear.value = newDate.year
  currentMonth.value = newDate.month
}

/**
 * @description 날짜 포맷 함수입니다.
 * @param _date<Date> 날짜
 */
const format = (_date: Date) => {
  return globalDateFormat(_date, locale.value)
}

/**
 * @description 캘린더 메뉴 Open 함수입니다.
 */
const handleMenuOpen = () => {
  isMenuOpened.value = true
}

/**
 * @description 캘린더 메뉴 Close 함수입니다.
 */
const handleMenuClose = () => {
  isMenuOpened.value = false

  isShowMonthPicker.value = false
  isShowYearPicker.value = false

  if (currentMonth.value !== getMonth(resultDate.value)) {
    currentMonth.value = getMonth(resultDate.value)
  }

  if (currentYear.value !== getYear(resultDate.value)) {
    currentYear.value = getYear(resultDate.value)
  }
}

/**
 * @description 월 선택 창으로 이동하는 함수입니다.
 */
const handleOpenMonthPicker = () => {
  isShowMonthPicker.value = true
  isShowYearPicker.value = false

  updateYearList(currentYear.value)
}

/**
 * @description 년도 선택 창으로 이동하는 함수입니다.
 */
const handleOpenYearPicker = () => {
  isShowYearPicker.value = true
  isShowMonthPicker.value = false
}

/**
 * @description 월 선택 함수입니다.
 * @param month<number> 선택 월
 */
const handleMonthSelect = (month: number) => {
  currentMonth.value = month
  isShowMonthPicker.value = false

  // DatePicker의 내부 상태 업데이트
  if (datePickerRef.value) {
    datePickerRef.value.setMonthYear({ month: month, year: currentYear.value })
  }
}

/**
 * @description 년도 선택 함수입니다.
 * @param year<number> 선택 년도
 */
const handleYearSelect = (year: number) => {
  currentYear.value = year
  isShowYearPicker.value = false
  isShowMonthPicker.value = true

  updateYearList(currentYear.value)
}

/**
 * @description 월 리스트 생성 함수입니다.
 */
const generateMonthList = () => {
  const minMonth =
    props.minDate && getYear(props.minDate) === currentYear.value
      ? getMonth(props.minDate)
      : 0
  const maxMonth =
    props.maxDate && getYear(props.maxDate) === currentYear.value
      ? getMonth(props.maxDate)
      : 11
  const length = maxMonth - minMonth + 1

  monthList.value = Array.from({ length }, (_, i) => minMonth + i)
}

/**
 * @description 날짜 범위 차이 계산 함수입니다.
 */
const getDateRangeDifference = () => {
  if (!props.minDate || !props.maxDate) {
    return null
  }

  const minYear = getYear(props.minDate)
  const maxYear = getYear(props.maxDate)
  const yearDiff = maxYear - minYear

  return {
    yearDiff,
    minYear,
    maxYear,
    minMonth: getMonth(props.minDate),
    maxMonth: getMonth(props.maxDate),
    isShortRange: yearDiff < 10, // 10년 미만인 경우 true, 10년 이상인 경우 false
  }
}

/**
 * @description 년도 리스트 생성 및 년도 네비게이션 버튼 비활성화 여부 체크 함수입니다.
 * @param year<number> 선택 년도
 */
const updateYearList = (year: number) => {
  const rangeInfo = getDateRangeDifference()

  isDisabledYearNav.value.prev = false
  isDisabledYearNav.value.next = false

  if (year === (rangeInfo?.minYear ?? 1970)) {
    isDisabledYearNav.value.prev = true
    currentYear.value = rangeInfo?.minYear ?? 1970

    generateMonthList()
    return
  } else if (year === (rangeInfo?.maxYear ?? getYear(new Date()))) {
    isDisabledYearNav.value.next = true
    currentYear.value = rangeInfo?.maxYear ?? getYear(new Date())

    generateMonthList()
    return
  } else {
    currentYear.value = year

    generateMonthList()
  }
}

/**
 * @description 선택 년도가 속한 00 ~ 09년의 범위로 10년 단위 년도 리스트 업데이트 함수입니다.
 * @param year<number> 선택 년도
 */
const updateDecade = (year: number) => {
  const rangeInfo = getDateRangeDifference()

  if (rangeInfo?.isShortRange) {
    const minYear = rangeInfo.minYear
    const maxYear = rangeInfo.maxYear

    // 현재 년도가 범위를 벗어나면 조정
    if (year < minYear) {
      year = minYear
    } else if (year > maxYear) {
      year = maxYear
    }

    // decade가 아니라 실제 년도 범위만 사용
    const start = minYear
    const end = maxYear

    isDisabledDecadeNav.value.prev = true
    isDisabledDecadeNav.value.next = true

    currentDecade.value = { start, end, year }

    // 실제 년도 리스트 생성 (minYear부터 maxYear까지)
    const length = end - start + 1
    currentDecadeList.value = Array.from({ length }, (_, i) => start + i)

    return
  }

  const firstYear = props.minDate
    ? Math.floor(getYear(props.minDate) / 10) * 10
    : 1970
  const lastYear = props.maxDate
    ? Math.floor(getYear(props.maxDate) / 10) * 10 + 9
    : Math.floor(getYear(new Date()) / 10) * 10 + 9
  const start = Math.floor(year / 10) * 10
  const end = start + 9

  if (start < firstYear) {
    return
  }
  if (end > lastYear) {
    return
  }

  isDisabledDecadeNav.value.prev = false
  isDisabledDecadeNav.value.next = false

  if (start <= firstYear) {
    isDisabledDecadeNav.value.prev = true
  }
  if (end >= lastYear) {
    isDisabledDecadeNav.value.next = true
  }

  const length = end - start + 1

  currentDecade.value = { start: start, end: end, year }
  currentDecadeList.value = Array.from({ length }, (_, i) => start + i)
}

watch(
  () => props.date,
  () => {
    setFormatDate()
  },
  { immediate: true }
)

onMounted(() => {
  setFormatDate()
})
</script>

<template>
  <VueDatePicker
    ref="datePickerRef"
    :model-value="new Date(resultDate)"
    :locale="locale"
    week-start="0"
    :year-first="true"
    :auto-apply="true"
    :enable-time-picker="false"
    :preview-format="format"
    :min-date="formatMinDate"
    :max-date="formatMaxDate"
    :hide-offset-dates="true"
    :prevent-min-max-navigation="true"
    :month-change-on-scroll="false"
    position="left"
    :auto-position="true"
    teleport="body"
    :offset="8"
    :ui="{
      menu:
        isShowMonthPicker || isShowYearPicker
          ? 'date-picker-menu-wrap date-picker-menu-wrap-month-year'
          : 'date-picker-menu-wrap',
    }"
    @update-month-year="changeMonthYear"
    @update:model-value="updateDate"
    @date-update="updateDate"
    @open="handleMenuOpen"
    @closed="handleMenuClose"
  >
    <template #trigger>
      <div
        class="relative flex items-center justify-between w-full h-[40px] px-[16px] border border-solid border-[1px] border-[#D9D9D9] rounded-[8px] bg-white cursor-pointer"
      >
        <AtomsInput
          :model-value="formatDate"
          :placeholder="formatPlaceholder"
          :value="formatDate"
          name="date-picker-text"
          label
          readonly
          class="inline-flex items-center justify-start !w-full !h-auto !p-0 m-0 bg-transparent border-none outline-none cursor-pointer text-left !text-[14px] text-[#333333] font-[400] !leading-[20px] !tracking-[-0.42px]"
        />
        <AtomsIconsCalendarLine
          :size="breakpoints.isMobile ? 16 : 20"
          color="#333333"
          class="absolute top-[50%] right-[12px] translate-y-[-50%] md:right-[16px]"
        />
      </div>
    </template>
    <template #calendar-header="{ index, day }">
      <div :class="index === 0 ? 'sunday' : index === 6 ? 'saturday' : ''">
        {{ day }}
      </div>
    </template>
    <template #day="{ day, date }">
      <span
        :class="[
          date.getDay() === 0 ? 'text-[#FC4420]' : 'text-[#333333]',
          date > new Date(formatMaxDate) ? 'opacity-35' : 'opacity-100',
        ]"
      >
        {{ day }}
      </span>
    </template>
    <template
      #month-year="{
        month,
        year,

        updateMonthYear,
        isDisabled,
      }"
    >
      <div class="flex items-center justify-between w-full py-[16px]">
        <button
          type="button"
          class="relative inline-flex items-center justify-center w-[32px] h-[32px]"
          :disabled="isDisabled(false)"
          @click="
            updateMonthYear(
              month === 0 ? 11 : month - 1,
              month === 0 ? year - 1 : year,
              false
            )
          "
        >
          <AtomsIconsArrowRightLine
            :size="16"
            :color="isDisabled(false) ? '#CCCCCC' : '#333333'"
            class="relative rotate-180"
          />
        </button>
        <button
          type="button"
          class="relative inline-flex items-center justify-center gap-[8px] w-auto h-[32px]"
          @click="handleOpenMonthPicker"
        >
          <span
            class="inline-flex items-center justify-center text-[#1F1F1F] text-[18px] font-[500] leading-[26px] tracking-[-0.54px]"
          >
            {{ t('Text_MonthYear', { month: month + 1, year: year }) }}
          </span>
          <AtomsIconsArrowDownFill :size="16" color="#333333" />
        </button>
        <button
          type="button"
          class="relative inline-flex items-center justify-center w-[32px] h-[32px]"
          :disabled="isDisabled(true)"
          @click="
            updateMonthYear(
              month === 11 ? 0 : month + 1,
              month === 11 ? year + 1 : year,
              false
            )
          "
        >
          <AtomsIconsArrowRightLine
            :size="16"
            :color="isDisabled(true) ? '#CCCCCC' : '#333333'"
            class="relative"
          />
        </button>
      </div>

      <div
        v-if="isShowMonthPicker && isMenuOpened"
        class="absolute z-[2] top-0 left-0 flex flex-col items-center justify-start w-full h-full overflow-y-auto bg-white"
      >
        <div class="flex items-center justify-between w-full py-[16px]">
          <button
            type="button"
            class="relative inline-flex items-center justify-center w-[32px] h-[32px]"
            :disabled="isDisabledYearNav.prev"
            @click="updateYearList(currentYear - 1)"
          >
            <AtomsIconsArrowRightLine
              :size="16"
              :color="isDisabledYearNav.prev ? '#CCCCCC' : '#333333'"
              class="relative rotate-180"
            />
          </button>
          <button
            type="button"
            class="relative inline-flex items-center justify-center gap-[8px] w-auto h-[32px]"
            @click="handleOpenYearPicker"
          >
            <span
              class="inline-flex items-center justify-center text-[#1F1F1F] text-[18px] font-[500] leading-[26px] tracking-[-0.54px]"
            >
              {{ currentYear }}
            </span>
          </button>
          <button
            type="button"
            class="relative inline-flex items-center justify-center w-[32px] h-[32px]"
            :disabled="isDisabledYearNav.next"
            @click="updateYearList(currentYear + 1)"
          >
            <AtomsIconsArrowRightLine
              :size="16"
              :color="isDisabledYearNav.next ? '#CCCCCC' : '#333333'"
              class="relative"
            />
          </button>
        </div>

        <div class="grid grid-cols-3 gap-[4px] w-full">
          <button
            v-for="month in monthList"
            :key="month"
            type="button"
            :class="[
              'inline-flex items-center justify-center w-full h-[52px] px-[12px] py-[8px] text-center text-[14px] font-[400] leading-[20px] tracking-[-0.42px] rounded-[4px] transition-colors',
              year === currentYear && month === currentMonth
                ? 'bg-[#FC4420] text-white'
                : 'text-[#333333] hover:bg-[#F5F5F5]',
            ]"
            @click="handleMonthSelect(month)"
          >
            {{ t('Text_Month', { month: month + 1 }) }}
          </button>
        </div>
      </div>

      <div
        v-if="isShowYearPicker && isMenuOpened"
        class="absolute z-[2] top-0 left-0 flex flex-col items-center justify-start w-full h-full overflow-y-auto bg-white"
      >
        <div class="flex items-center justify-between w-full py-[16px]">
          <button
            type="button"
            class="relative inline-flex items-center justify-center w-[32px] h-[32px]"
            :disabled="isDisabledDecadeNav.prev"
            @click="updateDecade(currentDecade.year - 10)"
          >
            <AtomsIconsArrowRightLine
              :size="16"
              :color="isDisabledDecadeNav.prev ? '#CCCCCC' : '#333333'"
              class="relative rotate-180"
            />
          </button>
          <button
            type="button"
            class="relative inline-flex items-center justify-center gap-[8px] w-auto h-[32px] cursor-default"
            :disabled="true"
          >
            <span
              class="inline-flex items-center justify-center text-[#1F1F1F] text-[18px] font-[500] leading-[26px] tracking-[-0.54px]"
            >
              {{
                t('Text_Decade', {
                  start: getDateRangeDifference()?.isShortRange
                    ? getDateRangeDifference()?.minYear
                    : currentDecade.start,
                  end: getDateRangeDifference()?.isShortRange
                    ? getDateRangeDifference()?.maxYear
                    : currentDecade.end,
                })
              }}
            </span>
          </button>
          <button
            type="button"
            class="relative inline-flex items-center justify-center w-[32px] h-[32px]"
            :disabled="isDisabledDecadeNav.next"
            @click="updateDecade(currentDecade.year + 10)"
          >
            <AtomsIconsArrowRightLine
              :size="16"
              :color="isDisabledDecadeNav.next ? '#CCCCCC' : '#333333'"
              class="relative"
            />
          </button>
        </div>

        <div class="grid grid-cols-3 gap-[4px] w-full">
          <button
            v-for="year in currentDecadeList"
            :key="year"
            type="button"
            :class="[
              'inline-flex items-center justify-center w-full h-[52px] px-[12px] py-[8px] text-center text-[14px] font-[400] leading-[20px] tracking-[-0.42px] rounded-[4px] transition-colors',
              year === currentYear
                ? 'bg-[#FC4420] text-white'
                : 'text-[#333333] hover:bg-[#F5F5F5]',
            ]"
            @click="handleYearSelect(year)"
          >
            {{ year }}
          </button>
        </div>
      </div>
    </template>
  </VueDatePicker>
</template>

<style>
body .date-picker-menu-wrap {
  padding: 0 12px 8px !important;
  border-radius: 8px !important;
  box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.1) !important;
}
body .date-picker-menu-wrap .dp__calendar_header_separator {
  display: none !important;
}
body .date-picker-menu-wrap .dp__menu {
  padding: 0 !important;
}
body .date-picker-menu-wrap .dp__menu,
body .date-picker-menu-wrap .dp__menu_wrap {
  transform: translateZ(0) !important;
  isolation: isolate !important;
}
body .date-picker-menu-wrap .dp__menu_inner {
  padding: 0 !important;
}
body .date-picker-menu-wrap .dp__calendar {
  padding: 0 !important;
}
body .date-picker-menu-wrap .dp__calendar_row {
  -webkit-gap: 4px !important;
  -moz-gap: 4px !important;
  -o-gap: 4px !important;
  -ms-gap: 4px !important;
  gap: 4px !important;
  margin: 0 !important;
}
body .date-picker-menu-wrap .dp__calendar_item {
  padding: 2px 4px !important;
}
body .date-picker-menu-wrap .dp__cell_inner {
  padding: 0px !important;
  border-radius: 100% !important;
  outline: none !important;
  border: none !important;
}
body .date-picker-menu-wrap .dp__cell_inner.dp__date_hover:hover {
  background-color: rgba(0, 0, 0, 0.04) !important;
}
body .date-picker-menu-wrap .dp__cell_inner.dp__today {
  border: 1px solid rgba(0, 0, 0, 0.15) !important;
}
body .date-picker-menu-wrap .dp__cell_inner.dp__active_date {
  background-color: #fc4420 !important;
  color: #ffffff !important;
}
body .date-picker-menu-wrap .dp__cell_inner.dp__active_date span {
  color: #ffffff !important;
}
body .date-picker-menu-wrap .dp__cell_disabled:hover,
body .date-picker-menu-wrap .dp__cell_disabled {
  cursor: default !important;
  background-color: transparent !important;
}
body .date-picker-menu-wrap .dp__menu,
body .date-picker-menu-wrap .dp__menu_wrap,
body .date-picker-menu-wrap .dp__overlay {
  box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.1) !important;
}
body .date-picker-menu-wrap .dp__overlay_cell_pad {
  color: #333333 !important;
}
body .date-picker-menu-wrap .dp__overlay_cell_pad:hover {
  background-color: #f5f5f5 !important;
}
body .date-picker-menu-wrap .dp__overlay_cell_pad.dp__overlay_cell_active {
  background-color: #fc4420 !important;
  color: #ffffff !important;
}
body
  .date-picker-menu-wrap
  .dp__overlay_cell_pad.dp__overlay_cell_disabled:hover,
body .date-picker-menu-wrap .dp__overlay_cell_pad.dp__overlay_cell_disabled {
  cursor: default !important;
  background-color: transparent !important;
  opacity: 0.35 !important;
}
body .date-picker-menu-wrap .dp__inner_nav {
  color: #333333 !important;
}
body .date-picker-menu-wrap .dp__inner_nav:hover {
  background-color: transparent !important;
}
body .date-picker-menu-wrap .dp__inner_nav.dp__inner_nav_disabled {
  background-color: transparent !important;
  cursor: default !important;
  opacity: 0.35 !important;
}
body .date-picker-menu-wrap .dp__month_year_select,
body .date-picker-menu-wrap .dp__month_year_select:hover {
  background-color: transparent !important;
}
body .date-picker-menu-wrap .dp__arrow_bottom,
body .date-picker-menu-wrap .dp__arrow_top,
body .date-picker-menu-wrap .dp__arrow_left,
body .date-picker-menu-wrap .dp__arrow_right {
  display: none !important;
}

.date-picker-wrap.date-picker-menu-absolute :deep(.dp__menu) {
  position: absolute !important;
}

.date-picker-wrap.date-picker-menu-fixed :deep(.dp__menu) {
  position: fixed !important;
}

body .date-picker-menu-wrap .dp__month_year_select {
  display: none !important;
}

body .date-picker-menu-wrap.date-picker-menu-wrap-month-year {
  height: 295px !important;
  overflow: hidden !important;
}
</style>
