<template>
  <div
    class="q-ma-sm q-gutter-sm"
    :style="{ height: props.height + 'px', width: props.width + 'px' }"
  >
    <h6 class="text-center text-bold q-ma-md text-primary">{{ props.title }}</h6>
    <div class="row">
      <q-checkbox
        style="min-width: 75px"
        dense
        v-for="opt in weekdayOptions"
        :key="opt.value"
        v-model="selectedWeekdays"
        :val="opt.value"
        :label="opt.label"
      />
      <q-tabs
        v-model="activeTab"
        dense
        class="text-primary"
        active-color="primary"
        indicator-color="primary"
        align="justify"
        narrow-indicator
        @update:model-value="onTabChange"
      >
        <q-tab no-caps name="today" :label="$t('today')" />
        <q-tab no-caps name="week" :label="$t('week')" />
        <q-tab no-caps name="month" :label="$t('month')" />
        <q-tab no-caps name="year" :label="$t('year')" />
      </q-tabs>
    </div>
    <div class="row">
      <q-date :locale="calendarLanguage" v-model="dateRange" range flat />
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, watch, type PropType, computed } from 'vue';
import { date } from 'quasar';
import { i18n } from 'src/boot/lang';
import type { QDateLocale } from 'src/vueLib/models/qDateLocale';
const props = defineProps({
  title: String,
  height: { type: Number, default: 400 },
  width: { type: Number, default: 300 },
});

const weekdays = defineModel('weekdays', {
  type: Array as PropType<number[]>,
  default: () => [0, 3],
});

const startDate = new Date();

const activeTab = ref('');
// Initial range (format: YYYY-MM-DD)
const dateRange = ref<string | { to: string; from: string }>('');
const selectedWeekdays = ref(weekdays); // Default to weekdays

const emit = defineEmits(['update:dates']);
onMounted(() => {
  dateRange.value = date.formatDate(startDate, 'YYYY-MM-DD');
});

const weekdayOptions = [
  { label: i18n.global.t('MondayShort'), value: 1 },
  { label: i18n.global.t('TuesdayShort'), value: 2 },
  { label: i18n.global.t('WednesdayShort'), value: 3 },
  { label: i18n.global.t('ThursdayShort'), value: 4 },
  { label: i18n.global.t('FridayShort'), value: 5 },
  { label: i18n.global.t('SaturdayShort'), value: 6 },
  { label: i18n.global.t('SundayShort'), value: 0 },
];

const calendarLanguage = computed(() => {
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
  const localeData = i18n.global.tm('calendar') as unknown as QDateLocale;
  return {
    days: localeData.days,
    daysShort: localeData.daysShort,
    months: localeData.months,
    monthsShort: localeData.monthsShort,
    firstDayOfWeek: localeData.firstDayOfWeek,
    format24h: localeData.format24h,
    pluralDay: localeData.pluralDay,
  };
});

const onTabChange = (val: 'today' | 'week' | 'month' | 'year') => {
  if (val) setRange(val);
  // Optional: Reset tab to empty so user can click the same tab again later
  setTimeout(() => {
    activeTab.value = '';
  }, 500);
};

// The Logic: Calculate all specific dates within the range that match weekdays
watch(dateRange, () => {
  if (!dateRange.value) {
    return [];
  } else if (typeof dateRange.value === 'string') {
    const current = new Date(dateRange.value);
    if (current !== undefined && selectedWeekdays.value.includes(current.getDay())) {
      emit('update:dates', [date.formatDate(current, 'YYYY-MM-DD')]);
      return [date.formatDate(current, 'YYYY-MM-DD')];
    }
    return [];
  }

  const end = new Date(dateRange.value.to);
  const result = [];

  let current = new Date(dateRange.value.from);

  while (current <= end) {
    if (selectedWeekdays.value.includes(current.getDay())) {
      result.push(date.formatDate(current, 'YYYY-MM-DD'));
    }
    current = date.addToDate(current, { days: 1 });
  }

  emit('update:dates', result);
  return result;
});

/**
 * Logic for 1 Week / 1 Month / 1 Year
 */
const setRange = (type: 'today' | 'week' | 'month' | 'year') => {
  const anchor =
    typeof dateRange.value === 'string'
      ? new Date(dateRange.value)
      : new Date(dateRange.value.from);

  let from: Date;
  let to: Date;

  if (type === 'today') {
    const now = new Date();
    dateRange.value = date.formatDate(date.startOfDate(now, 'day'), 'YYYY-MM-DD');

    return;
  } else if (type === 'week') {
    // getDay() returns 0 for Sunday.
    // We calculate how many days to subtract to get to Monday (1).
    const day = anchor.getDay();
    const diffToMonday = day === 0 ? -6 : 1 - day;

    from = date.addToDate(anchor, { days: diffToMonday });
    from = date.startOfDate(from, 'day'); // Reset time to 00:00

    to = date.addToDate(from, { days: 6 });
    to = date.endOfDate(to, 'day'); // Set time to 23:59
  } else if (type === 'month') {
    // 'month' is a valid unit for startOfDate
    from = date.startOfDate(anchor, 'month');
    to = date.endOfDate(anchor, 'month');
  } else {
    // 'year' is a valid unit for startOfDate
    from = date.startOfDate(anchor, 'year');
    to = date.endOfDate(anchor, 'year');
  }

  dateRange.value = {
    from: date.formatDate(from, 'YYYY/MM/DD'),
    to: date.formatDate(to, 'YYYY/MM/DD'),
  };
};

watch(
  [dateRange, selectedWeekdays],
  () => {
    if (!dateRange.value) {
      emit('update:dates', []);
      return;
    }

    let start: Date, end: Date;

    if (typeof dateRange.value === 'string') {
      start = new Date(dateRange.value);
      end = new Date(dateRange.value);
    } else {
      start = new Date(dateRange.value.from);
      end = new Date(dateRange.value.to);
    }

    const result = [];
    let current = start;

    while (current <= end) {
      if (selectedWeekdays.value.includes(current.getDay())) {
        result.push(date.formatDate(current, 'YYYY-MM-DD'));
      }
      current = date.addToDate(current, { days: 1 });
    }

    emit('update:dates', result);
  },
  { deep: true },
);
</script>
