<template>
  <div class="cpCalendar">
    <cp-input
      :id="triggerElementId"
      class="cpCalendar__input"
      :disabled="isDisabled"
      :error-message="errorMessage"
      :is-invalid="isError"
      :label="label"
      :model-value="humanDateFormat(dateOne, dateTwo)"
      placeholder="Select a date"
      type="text"
    />
    <core-datepicker
      class="cpCalendar__datepicker"
      :class="dynamicClasses"
      :close-after-select="closeAfterSelect"
      :close-calendar="triggerCalendar"
      :customized-dates="[{ cssClass: 'recurency', dates: datesToStringArray(recurenceDates) }]"
      :date-one="dateOne"
      :date-two="dateTwo"
      :inline="isInline"
      :locale="locale"
      :mode="mode"
      :trigger-element-id="triggerElementId"
      @closed="() => (isDisabled = false)"
      @date-one-selected="onDateSelected('dateOne', $event)"
      @date-two-selected="onDateSelected('dateTwo', $event)"
      @is-inline="onIsInline"
      @opened="() => (isDisabled = true)"
    >
      <div v-if="!isInline" class="footer-container">
        <transition-group name="fade">
          <div v-if="isDaysDisplayed" key="days" class="asd__recurency--container">
            <div class="asd__recurency--days">
              <ul class="asd__recurency--ul">
                <li
                  v-for="({ day, selected, value }, index) in days"
                  :key="index"
                  class="asd__recurency--li"
                  :class="{ 'asd__recurency--li--selected': selected }"
                  @click="toggleDay(value)"
                >
                  <div class="checkbox">
                    <transition mode="out-in" name="fade">
                      <div v-if="!selected" class="checkbox--border" />
                      <cp-icon v-else type="check" />
                    </transition>
                  </div>
                  <span>{{ day }}</span>
                </li>
              </ul>
            </div>
            <div class="asd__recurency--summary">
              <div class="asd__recurency--text">
                <cp-icon v-if="recurenceDates.length > 0" type="refresh-cw" />
                <span v-if="recurenceDates.length > 0">
                  Between the
                  <strong>{{ summary.start }}</strong> and the
                  <strong>{{ summary.end }}</strong>
                </span>
                <span v-else>Please select at least one day.</span>
              </div>
            </div>
          </div>
          <div key="action" class="asd__recurency--action">
            <cp-button class="asd__button-switch" @click.stop="handleRecurency">
              {{ isDaysDisplayed ? 'Remove recurency' : 'Add recurency' }}
            </cp-button>
            <cp-button
              class="asd__button-done"
              :disabled="(isDaysDisplayed && !recurenceDates.length) || !dateOne"
              @click.stop="done"
            >
              Done
            </cp-button>
          </div>
        </transition-group>
      </div>
    </core-datepicker>
  </div>
</template>

<script setup lang="ts">
import { DateTime, Info, Interval } from 'luxon'
import { computed, ref, useId, watch } from 'vue'

import CpButton from '@/components/CpButton.vue'
import CpIcon from '@/components/CpIcon.vue'
import CpInput from '@/components/CpInput.vue'

import { formatDates } from '@/helpers'
import CoreDatepicker from '@/libs/CoreDatepicker.vue'

interface DayOption {
  day: string
  selected: boolean
  value: number
}

interface Props {
  closeAfterSelect?: boolean
  errorMessage?: string
  initDateOne?: string
  isError?: boolean
  label?: string
  locale?: string
  triggerElementId?: string
}

interface Emits {
  (e: 'dates', dates: string[]): void
}

const props = withDefaults(defineProps<Props>(), {
  triggerElementId: () => `calendar-trigger-${useId()}`,
  closeAfterSelect: false,
  label: '',
  isError: false,
  errorMessage: '',
  initDateOne: '',
  locale: () => navigator.language,
})

const emit = defineEmits<Emits>()

const humanFormat = 'EEE DD'
const dateOne = ref(props.initDateOne || '')
const dateTwo = ref('')
const days = ref<DayOption[]>(
  Info.weekdays('short', { locale: 'en-EN' }).map((day: string, value: number) => ({ day, selected: false, value })),
)
const recurenceDates = ref<string[]>([])
const mode = ref<'single' | 'range'>('single')
const triggerCalendar = ref(false)
const isInline = ref(false)
const isDisabled = ref(false)

const selectedDays = computed(() => {
  return days.value.filter((day) => day.selected === true).map((day) => day.value + 1)
})

const summary = computed(() => {
  const start = DateTime.fromISO(dateOne.value).toFormat(humanFormat)
  const end = DateTime.fromISO(dateTwo.value).toFormat(humanFormat)

  return {
    start,
    end,
  }
})

const isDaysDisplayed = computed(() => {
  return mode.value === 'range'
})

const dynamicClasses = computed(() => {
  return { 'cpCalendar__datepicker--isInline': isInline.value }
})

const done = (): void => {
  emitToParent()
  closeCalendar()
}

const onDateSelected = (dateSelector: 'dateOne' | 'dateTwo', date: string) => selectDate(dateSelector, date)

const onIsInline = (value: boolean) => (isInline.value = value)

const closeCalendar = (): void => {
  triggerCalendar.value = !triggerCalendar.value
}

const humanDateFormat = (dateOne: string, dateTwo: string): string => {
  return formatDates({ dateOne, dateTwo, format: humanFormat, locale: props.locale })
}

const toggleDay = (index: number): void => {
  days.value[index].selected = !days.value[index].selected
  recurenceDates.value = generateDates(dateOne.value, dateTwo.value, selectedDays.value)
  emitToParent()
}

const handleRecurency = (): void => {
  if (isDaysDisplayed.value) {
    removeRecurency()
  } else {
    addRecurency()
  }
}

const addRecurency = (): void => {
  const dt = DateTime.local()
  mode.value = 'range'

  if (!dateOne.value) dateOne.value = dt.plus({ days: 1 }).toISODate()
  if (!dateTwo.value) dateTwo.value = DateTime.fromISO(dateOne.value).plus({ days: 7 }).toISODate()
}

const removeRecurency = (): void => {
  mode.value = 'single'
  recurenceDates.value = []
  dateTwo.value = ''
  days.value = Info.weekdays('short', { locale: 'en-EN' }).map((day: string, value: number) => {
    return { day, selected: false, value }
  })
}

const emitToParent = (): void => {
  let dates = [dateOne.value]

  if (isDaysDisplayed.value) {
    dates = recurenceDates.value.length ? datesToStringArray(recurenceDates.value) : []
  }

  emit('dates', dates)
}

const generateDates = (startDate: string, endDate: string, selectedWeekDaysIndex: number[]): string[] => {
  const isoStartDate = DateTime.fromISO(startDate)
  const isoEndDate = DateTime.fromISO(endDate)

  let selectedDaysFinalList: string[] = []

  const daysIntervalBetweenTwoDates = Interval.fromDateTimes(isoStartDate.startOf('day'), isoEndDate.endOf('day'))
    .splitBy({ days: 1 })
    .map((day: typeof Interval) => day.start)

  daysIntervalBetweenTwoDates.forEach((day: string) => {
    const dayOfWeekAsNumber = Number(DateTime.fromISO(day).toFormat('c'))

    if (selectedWeekDaysIndex.includes(dayOfWeekAsNumber)) {
      selectedDaysFinalList.push(day)
    }
  })

  return selectedDaysFinalList
}

const selectDate = (key: 'dateOne' | 'dateTwo', value: string): void => {
  if (key === 'dateOne') {
    dateOne.value = value
  } else {
    dateTwo.value = value
  }

  if (isDaysDisplayed.value) {
    recurenceDates.value = generateDates(dateOne.value, dateTwo.value, selectedDays.value)
  }

  emitToParent()
}

const datesToStringArray = (dates: string[]): string[] => {
  return dates.map((date) => DateTime.fromISO(date).toISODate() || '')
}

watch(isInline, (value) => {
  if (value) removeRecurency()
})
</script>

<style lang="scss">
// Transition
.fade-enter-active,
.fade-leave-active {
  transition: opacity 100ms;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}

.cpCalendar {
  // Opening transition layout adaptation
  .asd__fade-enter-from {
    transform: translateX(-50%) scale(0.8);
  }

  .asd__fade-enter-to {
    transform: translateX(-50%) scale(1);
  }

  &__datepicker {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
  }

  &__datepicker:not(&__datepicker--isInline) {
    margin-top: var(--cp-dimensions-2_5);
  }

  @media (min-width: 350px) {
    position: relative;
  }

  @media (max-width: 767px) and (min-width: 350px) {
    &__datepicker {
      right: 0;
      left: 0;
      transform: initial;
    }

    &__datepicker:not(&__datepicker--isInline) {
      margin-top: var(--cp-spacing-xl);
    }

    // Opening transition layout adaptation
    .asd__fade-enter-from {
      transform: scale(0.8);
    }

    .asd__fade-enter-to {
      transform: scale(1);
    }
  }

  &__input input:disabled {
    background-color: var(--cp-background-white);
  }

  .footer-container {
    border-top: 1px solid var(--cp-border-soft);
    background: var(--cp-background-white);

    .asd__recurency--action {
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: var(--cp-spacing-xl);

      button {
        color: var(--cp-text-primary);
        font-size: var(--cp-text-size-md);
      }
    }

    .asd__button-done {
      border: #b5b5b5;

      &:not([disabled]) {
        border: var(--cp-background-accent-solid);
        background-color: var(--cp-background-accent-solid);
        color: var(--cp-text-white) !important;
      }
    }

    .asd__recurency--ul {
      display: flex;
      height: inherit;
      margin: 0;
      margin-top: -1px;
      cursor: pointer;
      -webkit-padding-start: 0;

      @media (min-width: 768px) {
        height: var(--cp-dimensions-16);
      }

      @media (max-width: 768px) {
        display: flex;
        flex-wrap: wrap;
      }
    }

    .asd__recurency--li {
      display: flex;
      height: var(--cp-dimensions-16);
      flex: 1;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      border-top: 1px solid var(--cp-border-soft);
      color: var(--cp-text-primary);
      font-size: var(--cp-text-size-md);
      transition: background 200ms linear;

      @media (max-width: 768px) {
        flex: 1 0 50%;
      }

      .checkbox {
        width: var(--cp-dimensions-4);
        height: var(--cp-dimensions-4);
      }

      .checkbox--border {
        width: 100%;
        height: 100%;
        border: 1px solid var(--cp-border-soft);
      }

      .feather-check {
        width: 100%;
        height: 100%;
        color: var(--cp-text-accent-primary);
      }

      span {
        padding-top: calc(var(--cp-dimensions-0_5) * 1.5);
        font-size: var(--cp-text-size-sm);
        line-height: var(--cp-line-height-sm);
        user-select: none;
      }
    }

    .asd__recurency--li--selected {
      color: var(--cp-text-accent-primary);
    }

    .asd__recurency--summary {
      padding-right: var(--cp-spacing-xl);
      padding-left: var(--cp-spacing-xl);
    }

    .asd__recurency--text {
      display: flex;
      align-items: center;
      justify-content: center;
      border-radius: var(--cp-radius-md-lg);
      background: var(--cp-text-primary);
      color: #d8d8ff;

      svg {
        transform: scale(0.7);
      }

      span {
        padding: var(--cp-dimensions-2_5);
        padding-left: var(--cp-dimensions-0_5);
        font-size: var(--cp-text-size-sm);
        white-space: normal;
      }

      span strong {
        color: var(--cp-text-white);
      }
    }
  }
}
</style>
