<template>
    <div class="max-w-8xl relative mx-auto flex flex-nowrap bg-white px-3 py-1 dark:bg-gray-900">
        <!-- Calendar Area -->
        <div class="min-w-[480px] flex-grow">
            <!-- Header with month navigation and filter icon -->
            <div class="mb-1 flex items-center justify-start">
                <button
                    @click="prevMonth"
                    :disabled="!canGoPrev"
                    class="mb-2 mr-4 p-1 focus:outline-none"
                    :class="{
                        'hover:rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700': canGoPrev,
                        'cursor-not-allowed text-gray-400 dark:text-gray-600': !canGoPrev
                    }"
                    title="Previous Month">
                    <ArrowLeftIcon class="text-gray-700 dark:text-gray-300" />
                </button>
                <div class="min-w-[10rem] text-center text-xl">
                    <span class="font-bold">{{ monthName }} {{ year }}</span>
                </div>
                <button
                    @click="nextMonth"
                    :disabled="!canGoNext"
                    class="mb-2 ml-4 p-1 focus:outline-none"
                    :class="{
                        'hover:rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700': canGoNext,
                        'cursor-not-allowed text-gray-400 dark:text-gray-600': !canGoNext
                    }"
                    title="Next Month">
                    <ArrowRightIcon class="text-gray-700 dark:text-gray-300" />
                </button>

                <div class="ml-auto flex items-center space-x-2">
                    <!-- Birthday Badge -->
                    <a :href="birthdayUrl" target="_blank">
                        <span
                            v-if="monthName === birthdayMonth"
                            class="inline-flex items-center rounded-md bg-mbs-moondance-yellow-50 px-2 py-1 text-xs font-medium text-mbs-moondance-yellow-700 ring-1 ring-inset ring-mbs-moondance-yellow-600/20">
                            <BirthdayIcon class="mr-1 h-4 w-4" />
                            Birthday Month
                        </span>
                    </a>
                    <!-- Room Badge -->

                    <a :href="roomOfferUrl" target="_blank">
                        <span
                            className="inline-flex items-center rounded-md bg-dexrm-50 px-2 py-1 text-xs font-medium text-dexrm-700 ring-1 ring-inset ring-dexrm-600/20">
                            <BedIcon class="mr-1 h-4 w-4" />
                            Room Offer Available
                        </span>
                    </a>

                    <!-- Filter Icon Button -->
                    <button
                        @click="toggleFilterSidebar"
                        class="ml-auto flex h-6 w-6 items-center justify-center rounded border border-gray-200 p-1 focus:outline-none dark:border-gray-700"
                        :class="{
                            'bg-gray-200 dark:bg-gray-700': showFilterSidebar,
                            'hover:bg-gray-100 dark:hover:bg-gray-600': !showFilterSidebar
                        }"
                        title="Toggle Filters">
                        <FilterIcon class="h-5 w-5 text-gray-700 dark:text-gray-200" />
                    </button>
                </div>
            </div>

            <!-- Day-of-week header -->
            <div
                class="grid grid-cols-7 gap-px border border-gray-200 bg-gray-200 text-left text-sm dark:border-gray-700 dark:bg-gray-700">
                <div
                    class="bg-white py-1 pl-1 text-gray-900 dark:bg-gray-950 dark:text-gray-100"
                    v-for="(dow, dowIndex) in dayOfWeek"
                    :key="dowIndex">
                    {{ dow }}
                </div>
            </div>

            <!-- Calendar Rows -->
            <div
                v-for="(row, rowIndex) in rows"
                :key="rowIndex"
                class="relative grid grid-cols-7 gap-px border border-t-0 border-gray-200 bg-gray-200 first:border-t dark:border-gray-700 dark:bg-gray-700">
                <!-- Day Cells -->
                <div
                    v-for="(day, colIndex) in row.days"
                    :key="colIndex"
                    :class="[
                        dayCellHeight,
                        'p-1',
                        'text-xs',
                        'relative',
                        'text-gray-900 dark:text-gray-100',
                        day.dimmed ? 'bg-gray-50 dark:bg-gray-800' : 'bg-white dark:bg-gray-950'
                    ]">
                    <div class="flex items-center">
                        <div
                            :class="
                                getDayNumberClasses({
                                    isToday: day.isToday ?? false,
                                    dimmed: day.dimmed
                                })
                            ">
                            {{ day.label }}
                        </div>

                        <div class="ml-1">
                            <span
                                v-if="day.monthLabel"
                                :class="[
                                    'text-sm font-bold text-gray-900 dark:text-white',
                                    { 'text-gray-500 opacity-50 dark:text-gray-400': day.dimmed }
                                ]">
                                {{ day.monthLabel }}
                            </span>
                        </div>

                        <div class="ml-auto flex items-center space-x-1">
                            <template v-for="iconCat in dayCellIconCategories" :key="iconCat.name">
                                <div
                                    v-if="
                                        getEventInfoForDayByCategory(
                                            colIndex,
                                            row.events,
                                            iconCat.name
                                        )
                                    ">
                                    <span
                                        class="inline-flex cursor-pointer items-center justify-end rounded border px-0.5 py-0 text-gray-800"
                                        :style="{
                                            borderColor: getDarkerCategoryActualColor(iconCat.name)
                                        }"
                                        :title="
                                            getEventInfoForDayByCategory(
                                                colIndex,
                                                row.events,
                                                iconCat.name
                                            )?.title ?? ''
                                        "
                                        :data-event-id="
                                            getEventInfoForDayByCategory(
                                                colIndex,
                                                row.events,
                                                iconCat.name
                                            )?.id
                                        "
                                        @click="handleCategoryIconClick($event)">
                                        <component
                                            :is="iconCat.component"
                                            class="h-4 w-4"
                                            aria-hidden="true"
                                            :style="{
                                                stroke: getDarkerCategoryActualColor(iconCat.name)
                                            }" />
                                    </span>
                                </div>
                            </template>
                        </div>
                    </div>
                    <button
                        v-if="hiddenEventCount(row, colIndex) > 0"
                        @click="openPopup(row, colIndex)"
                        class="absolute bottom-0 left-0 w-[calc(100%-8px)] border-0 bg-gray-200 p-1 text-center text-xs leading-none text-gray-800 hover:bg-gray-300 focus:outline-none dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600">
                        +{{ hiddenEventCount(row, colIndex) }}
                    </button>
                </div>

                <!-- Events -->
                <div
                    v-for="(event, eIndex) in row.events.filter(
                        (e) => !excludedCatogories.includes(e.category)
                    )"
                    :key="event.id + '-' + rowIndex"
                    @click="openEventPopup(event)"
                    @mouseenter="hoveredEventId = event.id"
                    @mouseleave="hoveredEventId = null"
                    class="!leading-1 absolute left-0 right-0 cursor-pointer items-center overflow-hidden px-1 py-[2px] text-xs duration-150 ease-in-out"
                    :style="{
                        gridColumnStart: event.colStart,
                        gridColumnEnd: `span ${event.colSpan}`,
                        top: `calc(${event.topOffset / 4}rem)`,
                        visibility:
                            event.topOffset >= eventVisibilityThreshold ? 'hidden' : 'visible',
                        borderColor: getDarkerCategoryActualColor(event.category)
                    }"
                    :class="[
                        `${categoryColors[event.category]} dark:${categoryDarkerColors[event.category]}`,
                        hoveredEventId === event.id
                            ? 'z-10 ring-1 ring-gray-500/50 ring-offset-1 dark:ring-gray-400/50 dark:ring-offset-black'
                            : '',
                        'border',
                        { 'w-[calc(100%-8px)]': isEventEndingOnThisSegment(event, row) }
                    ]"
                    :title="event.title">
                    <div
                        v-if="event.filterStatus"
                        :class="[
                            'absolute bottom-0 left-0 top-0 w-1.5',
                            getStatusIndicatorColorClass(event)
                        ]"
                        :style="getStatusIndicatorPatternStyle(event)"></div>
                    <div
                        :class="[
                            { 'ml-1': !event.filterStatus, 'ml-1.5': event.filterStatus },
                            'truncate'
                        ]">
                        <span>{{ event.title }}</span>
                    </div>
                </div>
            </div>

            <!-- Event Details Popup Modal -->
            <EventDetailsModal
                v-model="showEventPopup"
                :event="selectedEvent"
                :day-label="dayLabelForDetailsBreadcrumb ?? undefined"
                @breadcrumb-click="handleDetailsBreadcrumbClick" />
        </div>
        <!-- Right Column (Filters + Monthly List) -->
        <div
            v-show="showFilterSidebar"
            class="ml-6 mt-[6px] flex w-64 flex-shrink-0 flex-col duration-700 ease-in-out">
            <!-- Filter Sidebar -->
            <div class="flex-shrink-0">
                <!-- Rule Filter -->
                <FilterSection title="Type" :items="availableRules" v-model="selectedRules" />
                <!-- Category Filter -->
                <FilterSection
                    title="Category"
                    :items="availableCategories"
                    item-value-key="name"
                    item-label-key="name"
                    v-model="selectedCategories">
                    <template #itemDisplay="{ item }">
                        <template v-if="item && typeof item === 'object' && 'name' in item">
                            <template
                                v-if="excludedCatogories.includes((item as CategoryItem).name)">
                                <span
                                    class="mr-1.5 inline-flex flex-shrink-0 items-center justify-center rounded border px-0.5 py-0"
                                    :style="{
                                        borderColor: getDarkerCategoryActualColor(
                                            (item as CategoryItem).name
                                        )
                                    }"
                                    :title="(item as CategoryItem).name">
                                    <component
                                        :is="
                                            getIconForExcludedCategory((item as CategoryItem).name)
                                        "
                                        class="h-4 w-4"
                                        aria-hidden="true"
                                        :style="{
                                            stroke: getDarkerCategoryActualColor(
                                                (item as CategoryItem).name
                                            )
                                        }" />
                                </span>
                                <span>{{ (item as CategoryItem).name }}</span>
                            </template>
                            <template v-else>
                                <span
                                    :class="[
                                        (item as CategoryItem).color ?? '',
                                        'mr-1.5 inline-block h-4 w-[22px] flex-shrink-0 rounded border'
                                    ]"
                                    :style="{
                                        borderColor: (item as CategoryItem).darkerActualColor
                                    }"></span>
                                <span>{{ (item as CategoryItem).name }}</span>
                            </template>
                        </template>
                    </template>
                </FilterSection>
                <!-- Status Filter -->
                <FilterSection title="Status" :items="availableStatuses" v-model="selectedStatuses">
                    <template #itemDisplay="{ item }">
                        <span
                            :class="[
                                'relative mr-1.5 h-4 w-2 flex-shrink-0 border border-mbs-bay-reflection-blue-tint-1',
                                getFilterStatusKeyClass(item as string)
                            ]"
                            :style="getPatternStyle(item as string)"></span>
                        <span>{{ item }}</span>
                    </template>
                </FilterSection>
            </div>

            <!-- Events List -->
            <div class="mt-2 flex h-[8rem] flex-grow flex-col">
                <div class="mb-2 flex space-x-2">
                    <button
                        @click="activeTab = 'upcoming'"
                        class="border-b-2 px-2 py-1 text-xs focus:outline-none"
                        :class="
                            activeTab === 'upcoming'
                                ? 'border-dexrm-700 font-bold text-dexrm-700'
                                : 'border-transparent hover:border-gray-300'
                        ">
                        Upcoming
                    </button>
                    <button
                        @click="activeTab = 'past'"
                        class="border-b-2 px-2 py-1 text-xs focus:outline-none"
                        :class="
                            activeTab === 'past'
                                ? 'border-dexrm-700 font-bold text-dexrm-700'
                                : 'border-transparent hover:border-gray-300'
                        ">
                        Past
                    </button>
                </div>
                <div
                    class="flex-grow overflow-y-auto border border-gray-200 bg-white pr-1 dark:border-gray-700 dark:bg-gray-800">
                    <div
                        v-if="filteredTabEvents.length === 0"
                        class="py-2 text-center text-sm italic text-gray-500">
                        No {{ activeTab }} events for this month.
                    </div>
                    <div
                        v-for="event in filteredTabEvents"
                        :key="'list-' + event.id"
                        @click="openEventPopup(event)"
                        class="block cursor-pointer rounded border-b border-gray-200 p-2 text-xs text-gray-900 hover:bg-gray-50 dark:border-gray-700 dark:text-gray-100 dark:hover:bg-gray-700"
                        :title="`${event.title} (${formatEventDateRange(event.start, event.end)})`">
                        <div class="mb-0.5 flex items-center">
                            <!-- Status Indicator -->
                            <span
                                :class="[
                                    'mr-1.5 inline-block h-2.5 w-2.5 flex-shrink-0 rounded border',
                                    getStatusIndicatorColorClass(event)
                                ]"
                                :style="[
                                    getStatusIndicatorPatternStyle(event),
                                    { borderColor: getDarkerCategoryActualColor(event.category) }
                                ]"></span>
                            <span class="flex-grow truncate font-medium">{{ event.title }}</span>
                        </div>
                        <div
                            class="flex items-center space-x-1.5 pl-[16px] text-[11px] text-gray-500">
                            <span>{{ formatEventDateRange(event.start, event.end) }}</span>
                            <span
                                v-if="isEventOngoing(event)"
                                class="inline-flex items-center rounded-full px-1.5 py-0.5 text-[9px] font-medium text-dexrm-700 ring-1 ring-inset ring-dexrm-300">
                                Ongoing
                            </span>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <!-- More Events Popup Modal -->
        <MoreEventsModal
            v-model:show="showPopup"
            :day-label="popupDayLabel"
            :events="popupEvents"
            :excluded-categories="excludedCatogories"
            @event-click="handleModalEventClick" />
    </div>
</template>

<script setup lang="ts">
import useDashboardQuery from '@/modules/patron-dashboard/services/useDashboardQuery'
import api_endpoints from '@/core/utils/endpoints'
import { useDashboardStore } from '@/modules/patron-dashboard/stores/useDashboardStore'
import { ref, computed, withDefaults, defineProps, defineEmits, onMounted } from 'vue'
import type { RouteParams } from 'vue-router'
import {
    format,
    getMonth,
    getYear,
    subMonths,
    addMonths,
    startOfMonth,
    addDays,
    subDays,
    isAfter,
    isBefore,
    startOfToday,
    endOfDay,
    startOfDay,
    parse,
    isValid
} from 'date-fns'
import type {
    Event,
    EventWithLayout,
    CategoryItem
} from '@/modules/patron-calendar/types/calendarTypes'
//import defaultEventsData from '@/modules/patron-calendar/data/calendarEvent'
import {
    categoryColors,
    statusColors,
    eventPatterns,
    categoryDarkerColors,
    categoryDarkerActualColors,
    excludedCatogories,
    excludedCategoryIconsMap,
    dayCellIconCategories,
    MD_BREAKPOINT
} from '@/modules/patron-calendar/config/calendarConfig'
import {
    sortBasedOnOrder,
    parseDate,
    formatEventDateRange,
    isEventEndingOnThisSegment,
    filterEvents,
    getEventsForMonthView,
    getEventsForDay,
    generateCalendarGrid,
    getEventVisibilityOffsetThreshold,
    getDayCellHeight
    // EVENT_VISIBILITY_OFFSET_THRESHOLD
} from '@/modules/patron-calendar/services/calendarUtils'
import {
    getStatusColor,
    getPatternStyle,
    getDarkerCategoryColorClass,
    getDarkerCategoryActualColor,
    getFilterStatusKeyClass,
    getDayNumberClasses
} from '@/modules/patron-calendar/services/calendarStyleUtils'

import ArrowLeftIcon from '@/modules/patron-calendar/components/icons/ArrowLeftIcon.vue'
import ArrowRightIcon from '@/modules/patron-calendar/components/icons/ArrowRightIcon.vue'
import FilterIcon from '@/modules/patron-calendar/components/icons/FilterIcon.vue'
import BirthdayIcon from '@/modules/patron-calendar/components/icons/BirthdayIcon.vue'
import BedIcon from '@/modules/patron-calendar/components/icons/BedIcon.vue'
import { get } from '@vueuse/core'

const dashboardStore = useDashboardStore()
const { isEditing } = storeToRefs(dashboardStore)

const { data, error, isRefetching, isLoading, refetch } = useDashboardQuery<Event[]>(
    api_endpoints.Calendar_GetEvents
)

defineExpose({ refetch, isRefetching })

const emit = defineEmits<{
    (e: 'event-click', payload: { event: Event; dayLabel: string }): void
    (e: 'refreshed', error: APIError | null): void
}>()

watch(
    data,
    (newData) => {
        if (newData) {
            selectedCategories.value = availableCategories.value.map((cat) => cat.name)
            selectedStatuses.value = [...availableStatuses.value]
            selectedRules.value = [...availableRules.value]
        }
    },
    { immediate: true }
)

watch(isRefetching, (val) => {
    if (!val) {
        emit('refreshed', error.value)
    }
})

const eventVisibilityThreshold = computed(() =>
    getEventVisibilityOffsetThreshold(year.value, month.value)
)

//console.log('count:', data?.value?.length)

const props = withDefaults(
    defineProps<{
        initialMonth?: number
        initialYear?: number
        events?: Event[]
    }>(),
    {
        initialMonth: () => getMonth(new Date()) + 1,
        initialYear: () => getYear(new Date()),
        //events: () => defaultEventsData
        events: () => []
    }
)

const actualEvents = computed(() => (props.events.length > 0 ? props.events : data.value || []))
//console.log('actual : ' + actualEvents.value.length)

const route = useRoute()
const params = route.params as RouteParams<'/patron/dashboard/[id]'>

const roomOfferUrl = computed(
    () => `https://sbmkttst02/OfferWeb/RoomOffer/RoomOffer?ID=${params.id}`
)

const birthdayUrl = computed(() => `https://sbmkttst02/OfferWeb/Birthday/Birthday_Main`)

const MoreEventsModal = defineAsyncComponent(
    () => import('@/modules/patron-calendar/components/modals/MoreEvents.vue')
)

const EventDetailsModal = defineAsyncComponent(
    () => import('@/modules/patron-calendar/components/modals/EventDetails.vue')
)

const FilterSection = defineAsyncComponent(
    () => import('@/modules/patron-calendar/components/calendar/FilterSection.vue')
)

const month = ref(props.initialMonth)
const year = ref(props.initialYear)
const dayOfWeek = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
const showPopup = ref(false)
const popupEvents = ref<Event[]>([])
const popupDayLabel = ref('')
const showEventPopup = ref(false)
const selectedEvent = ref<Event | null>(null)
const dayLabelForDetailsBreadcrumb = ref<string | null>(null)
const selectedCategories = ref<string[]>([])
const selectedStatuses = ref<string[]>([])
const selectedRules = ref<string[]>([])
const hoveredEventId = ref<string | number | null>(null)
const showFilterSidebar = ref(true)
const birthdayMonth = 'April'
const cachedPopupEvents = ref<Event[]>([])
const minAllowedDate = computed(() => startOfMonth(subMonths(new Date(), 1)))
const maxAllowedDate = computed(() => startOfMonth(addMonths(new Date(), 3)))
const isMobile = ref(false)

const currentViewDateStart = computed(() => startOfMonth(new Date(year.value, month.value - 1, 1)))
const canGoPrev = computed(() => isAfter(currentViewDateStart.value, minAllowedDate.value))
const canGoNext = computed(() => isBefore(currentViewDateStart.value, maxAllowedDate.value))
const monthName = computed(() => format(new Date(year.value, month.value - 1), 'LLLL'))
const availableRules = computed(() => {
    const rules = [...new Set(actualEvents.value.map((e) => e.rule))].filter(
        (rule): rule is string => !!rule
    )
    //console.log('Unique rules count:', rules.length)
    return sortBasedOnOrder(rules, ['SRC Only', 'SL Only'])
})
const availableCategories = computed<CategoryItem[]>(() => {
    const categories = [...new Set(actualEvents.value.map((e) => e.category).filter(Boolean))]
    //console.log('Unique categories count:', categories.length)

    const categoryObjects = categories.map((cat) => ({
        name: cat,
        color: categoryColors[cat] || categoryColors.Default,
        darkerActualColor: categoryDarkerActualColors[cat] || categoryDarkerActualColors.Default
    }))
    return sortBasedOnOrder(categoryObjects, ['Birthday', 'Room', 'Default'], (item) => item.name)
})
const availableStatuses = computed(() => {
    const statuses = [...new Set(actualEvents.value.map((e) => e.filterStatus))].filter(
        (status): status is string => !!status
    )
    //console.log('Unique statuses count:', statuses.length)
    return sortBasedOnOrder(statuses, ['Offered', 'Reserved', 'Redeemed'])
})
const filteredEvents = computed(() => {
    //console.log('Events before filter:', actualEvents.value.length)
    const filtered = filterEvents(
        actualEvents.value,
        selectedRules.value,
        selectedCategories.value,
        selectedStatuses.value
    )
    //console.log('Events after filter:', filtered.length)
    return filtered.map((event) => ({
        ...event,
        bgColor: categoryColors[event.category] || categoryColors.Default,
        bgDarkerColor: categoryDarkerColors[event.category] || categoryDarkerColors.Default,
        statusColor: getStatusColor(event.filterStatus ?? ''),
        darkerActualColor: getDarkerCategoryActualColor(event.category)
    }))
})
const activeTab = ref('upcoming')
const today = startOfToday()

const eventsThisMonth = computed(() =>
    getEventsForMonthView(filteredEvents.value, year.value, month.value)
)

const dayCellHeight = computed(() => getDayCellHeight(year.value, month.value))

const filteredTabEvents = computed(() => {
    return eventsThisMonth.value.filter((event) => {
        const eventEndDate = parseDate(event.end)
        if (!eventEndDate) return false

        if (activeTab.value === 'upcoming') {
            return isAfter(eventEndDate, subDays(today, 1))
        } else {
            return isBefore(eventEndDate, today)
        }
    })
})

const rows = computed(() => generateCalendarGrid(year.value, month.value, filteredEvents.value))

const isEventUpcoming = (event: Event) => {
    if (!event || !event.start) {
        return false
    }
    const today = startOfToday()
    const eventStartDate = parseDate(event.start)
    if (!eventStartDate || !isValid(eventStartDate)) {
        return false
    }
    const sevenDaysLater = addDays(today, 7)
    return !isBefore(eventStartDate, today) && isBefore(eventStartDate, sevenDaysLater)
}

const isEventOngoing = (event: Event) => {
    if (!event || !event.start || !event.end) {
        return false
    }
    const now = new Date()
    const eventStartDate = parseDate(event.start)
    const eventEndDate = parseDate(event.end)
    if (!eventStartDate || !isValid(eventStartDate) || !eventEndDate || !isValid(eventEndDate)) {
        return false
    }
    return !isBefore(now, startOfDay(eventStartDate)) && !isAfter(now, endOfDay(eventEndDate))
}

const getIconForExcludedCategory = (categoryName: string) => {
    return excludedCategoryIconsMap[categoryName] || null
}

const getEventInfoForDayByCategory = (
    colIndex: number,
    eventsInWeek: EventWithLayout[] | undefined,
    category: string
) => {
    if (!eventsInWeek) return null
    const dayColumnNumber = colIndex + 1
    const matchingEvent = eventsInWeek.find((event) => {
        const eventStartsBeforeOrOnDay = event.colStart <= dayColumnNumber
        const eventEndsOnOrAfterDay = event.colStart + event.colSpan - 1 >= dayColumnNumber
        const overlapsDay = eventStartsBeforeOrOnDay && eventEndsOnOrAfterDay
        return event.category === category && overlapsDay
    })
    if (matchingEvent) {
        return {
            id: matchingEvent.id,
            title: matchingEvent.title
        }
    }
    return null
}

const handleCategoryIconClick = (event: MouseEvent) => {
    const eventId = (event.currentTarget as HTMLElement).dataset.eventId
    if (!eventId) {
        console.warn('Could not retrieve event ID from clicked category icon.')
        return
    }
    const eventData = filteredEvents.value.find((e) => String(e.id) === eventId)
    if (eventData) {
        openEventPopup(eventData)
    } else {
        console.warn(`Event with ID ${eventId} not found in filteredEvents.`)
    }
}

const handleModalEventClick = (payload: { event: Event; dayLabel: string }) => {
    selectedEvent.value = payload.event
    dayLabelForDetailsBreadcrumb.value = payload.dayLabel
    cachedPopupEvents.value = [...popupEvents.value]
    showPopup.value = false
    showEventPopup.value = true
}

const toggleFilterSidebar = () => {
    showFilterSidebar.value = !showFilterSidebar.value
}

const prevMonth = () => {
    if (!canGoPrev.value) return
    const newDate = subMonths(new Date(year.value, month.value - 1, 1), 1)
    month.value = getMonth(newDate) + 1
    year.value = getYear(newDate)
}

const nextMonth = () => {
    if (!canGoNext.value) return
    const newDate = addMonths(new Date(year.value, month.value - 1, 1), 1)
    month.value = getMonth(newDate) + 1
    year.value = getYear(newDate)
}

const hiddenEventCount = (row: { events: EventWithLayout[] }, colIndex: number) => {
    const eventsForDay = getEventsOverlappingColumn(row, colIndex)
    const hiddenEvents = eventsForDay.filter(
        (event) => event.topOffset >= getEventVisibilityOffsetThreshold(year.value, month.value)
    )
    return hiddenEvents.length
}

const getEventsOverlappingColumn = (row: { events: EventWithLayout[] }, colIndex: number) => {
    const colNumber = colIndex + 1
    return row.events.filter((event) => {
        return event.colStart <= colNumber && event.colStart + event.colSpan > colNumber
    })
}

const openPopup = (
    row: { days: { date: Date }[]; events: EventWithLayout[] },
    colIndex: number
) => {
    const day = row.days[colIndex]
    popupEvents.value = getEventsForDay(filteredEvents.value, day.date)
    popupDayLabel.value = format(day.date, 'PPPP')
    showEventPopup.value = false
    selectedEvent.value = null
    dayLabelForDetailsBreadcrumb.value = null
    cachedPopupEvents.value = [...popupEvents.value]
    showPopup.value = true
}

const handleDetailsBreadcrumbClick = () => {
    if (!dayLabelForDetailsBreadcrumb.value) return

    showEventPopup.value = false
    selectedEvent.value = null

    if (cachedPopupEvents.value && cachedPopupEvents.value.length > 0) {
        popupEvents.value = [...cachedPopupEvents.value]
    } else {
        const targetDate = parse(dayLabelForDetailsBreadcrumb.value!, 'PPPP', new Date())
        if (!isValid(targetDate)) {
            popupEvents.value = []
        } else {
            popupEvents.value = getEventsForDay(filteredEvents.value, targetDate)
        }
    }
    popupDayLabel.value = dayLabelForDetailsBreadcrumb.value
    showPopup.value = true
    dayLabelForDetailsBreadcrumb.value = null
    cachedPopupEvents.value = []
}

const openEventPopup = (event: Event) => {
    selectedEvent.value = event
    dayLabelForDetailsBreadcrumb.value = null
    showPopup.value = false
    showEventPopup.value = true
}

const getStatusIndicatorColorClass = (event: Event) => {
    if (!event || !event.filterStatus) {
        return ''
    }
    if (event.filterStatus === 'Redeemed' || event.filterStatus === 'Reserved') {
        return getDarkerCategoryColorClass(event.category)
    }
    return event.statusColor
}

const getStatusIndicatorPatternStyle = (event: Event) => {
    if (event && event.filterStatus && event.filterStatus !== 'Redeemed') {
        return getPatternStyle(event.filterStatus)
    }
    return {}
}

onMounted(() => {
    if (typeof window !== 'undefined') {
        isMobile.value = window.innerWidth < MD_BREAKPOINT
        showFilterSidebar.value = !isMobile.value
    }
})
</script>


based on above, generate a simple skeleton placeholder for the calendar only
use below template as reference

<template>
    <div class="h-full w-full px-0.5 py-1">
        <div class="flex h-full grow animate-pulse flex-wrap gap-1">
            <div class="flex h-full basis-1/6 flex-col gap-1">
                <div class="h-1/2 rounded-lg bg-slate-300 dark:bg-slate-700"></div>
                <div class="h-1/4 rounded-lg bg-slate-300 dark:bg-slate-700"></div>
                <div class="h-1/4 rounded-lg bg-slate-300 dark:bg-slate-700"></div>
            </div>
            <div class="flex h-full basis-1/4 flex-col gap-1">
                <div class="h-1/2 rounded-lg bg-slate-300 dark:bg-slate-700"></div>
                <div class="h-1/2 rounded-lg bg-slate-300 dark:bg-slate-700"></div>
            </div>
            <div class="flex h-full basis-1/4 flex-col gap-1">
                <div class="h-80 w-full rounded-lg bg-slate-300 dark:bg-slate-700"></div>
                <div class="h-80 w-full rounded-lg bg-slate-300 dark:bg-slate-700"></div>
                <div class="h-80 w-full rounded-lg bg-slate-300 dark:bg-slate-700"></div>
            </div>
            <div class="flex h-full grow rounded-lg bg-slate-300 dark:bg-slate-700"></div>
            <div class="fixed bottom-5 left-3 z-10 flex h-20 w-20 items-end justify-end p-2">
                <div
                    class="absolute flex items-center justify-center rounded-lg border-2 border-slate-100 bg-slate-300 p-4 text-gray-400 shadow-lg dark:border-slate-600 dark:bg-slate-700">
                    <i-fa6-solid-bars />
                </div>
            </div>
            <div class="fixed bottom-5 right-3 z-10 flex h-20 w-20 items-end justify-end p-2">
                <div
                    class="absolute flex items-center justify-center rounded-full border-2 border-slate-100 bg-slate-300 p-5 text-gray-400 shadow-lg dark:border-slate-600 dark:bg-slate-700">
                    <i-fa6-solid-pen-to-square />
                </div>
            </div>
        </div>
    </div>
</template>