<template>
  <div :class="cn.b()">
    <div :class="cn.e('header')">
      <UiButton
        icon="chevron-left"
        size="small"
        type="ghost"
        color="secondary"
        @click="previousMonth"
      />
      <p :class="cn.e('title')">
        {{ currentMonth }}
      </p>
      <UiButton
        icon="chevron-right"
        size="small"
        type="ghost"
        color="secondary"
        @click="nextMonth"
      />
    </div>
    <div :class="cn.e('week-days')">
      <div
        v-for="dayOfWeek in daysOfWeek"
        :key="dayOfWeek"
      >
        {{ dayOfWeek }}
      </div>
    </div>
    <div :class="cn.e('days')">
      <div
        v-for="day in monthDays"
        :key="day.date"
        :class="getDayClasses(day)"
        @click="selectDay(day)"
      >
        {{ day.day }}
      </div>
    </div>
    <UiButton
      type="outlined"
      color="secondary"
      :class="cn.e('apply-button')"
      @click="apply"
    >
      {{ $t('apply') }}
    </UiButton>
  </div>
</template>

<script setup>
// TODO: TypeScript
import { computed, reactive, ref, toRef, watch } from 'vue'
import dayjs from 'dayjs'
import UiButton from '../button/index.vue'
import useClassname from '../../composables/use-classname'

const props = defineProps({
  allowRangeSelection: {
    type: Boolean,
    default: true,
  },
  modelValue: {
    type: Array,
    default: () => [],
  },
})

const emit = defineEmits(['update:modelValue'])

const cn = useClassname('ui-calendar')

const modelValue = toRef(props, 'modelValue')

const today = dayjs()
const dateRange = reactive({
  start: null,
  end: null,
})
const currentDate = ref(today)
const daysOfWeek = dayjs.weekdaysMin()

const currentMonth = computed(() => currentDate.value.format('MMMM, YYYY'))

const monthDays = computed(() => {
  const daysInMonth = currentDate.value.daysInMonth()
  const leadingDays = currentDate.value.startOf('month').day()

  const days = []

  for (let i = 0; i < leadingDays; i++) {
    const date = currentDate.value
      .subtract(1, 'month')
      .endOf('month')
      .subtract(leadingDays - i - 1, 'day')

    days.push(date)
  }

  for (let i = 0; i < daysInMonth; i++) {
    const date = currentDate.value.date(i + 1)

    days.push(date)
  }

  const num = 7 - (days.length % 7 || 7)

  for (let i = 0; i < num; i++) {
    const date = currentDate.value.add(1, 'month').date(i + 1)

    days.push(date)
  }

  return days.map(formatDateObject)
})

function formatDateObject(date) {
  return {
    raw: date,
    day: date.date(),
    month: date.get('month'),
    isToday: date.isToday(),
    isCurrentMonth: date.isSame(currentDate.value, 'month'),
    isPeriod:
      !!dateRange.start
      && !!dateRange.end
      && date.isBetween(dateRange.start, dateRange.end, 'day', '[]'),
    isPeriodStart: !!dateRange.start && date.isSame(dateRange.start, 'day'),
    isPeriodEnd: !!dateRange.end && date.isSame(dateRange.end, 'day'),
  }
}

function nextMonth() {
  currentDate.value = currentDate.value.add(1, 'month')
}
function previousMonth() {
  currentDate.value = currentDate.value.subtract(1, 'month')
}

function selectDay(day) {
  let { start, end } = dateRange

  if (props.allowRangeSelection) {
    const isSameStartDay = day.raw.isSame(start, 'day')

    if (isSameStartDay) {
      start = null
      end = null
    }
    else if (!start || end) {
      start = day.raw
      end = null
    }
    else {
      end = day.raw

      if (dayjs(start).isAfter(dayjs(end), 'day'))
        [start, end] = [end, start]
    }
  }
  else {
    start = day.raw
    end = null
  }

  dateRange.start = start
  dateRange.end = end
}

watch(
  modelValue,
  (value) => {
    const [start, end] = value

    dateRange.start = !!start && dayjs(start)
    dateRange.end = !!end && dayjs(end)
  },
  { immediate: true },
)

function getDayClasses(day) {
  const classes = [cn.e('cell')]

  if (day.isCurrentMonth)
    classes.push(cn.em('cell', 'current-month'))

  if (day.isPeriod)
    classes.push(cn.em('cell', 'period'))

  if (day.isPeriodStart)
    classes.push(cn.em('cell', 'period-start'))
  else if (day.isPeriodEnd)
    classes.push(cn.em('cell', 'period-end'))

  if (day.isToday)
    classes.push(cn.em('cell', 'current-day'))

  return classes
}

function apply() {
  const value = []

  if (dateRange.start)
    value.push(dateRange.start.startOf('day').valueOf())

  if (dateRange.end)
    value.push(dateRange.end.startOf('day').valueOf())

  emit('update:modelValue', value)
}
</script>

<style lang="scss">
@use 'styles';
</style>
