<script setup lang="ts">
import type { Component } from 'vue'
import type { AvailabilitySlot, CalendarEvent, CalendarView, CalendarViewState, WeekStart } from './CalendarTypes'
import { timeDelta, Btn, ListItem, Dropdown, TabsNav, formatDate, useI18n } from '@bagelink/vue'
import { ref, computed, onMounted } from 'vue'
import CalendarPopover from './CalendarPopover.vue'
import AgendaView from './views/AgendaView.vue'
import DayView from './views/DayView.vue'
import MonthView from './views/MonthView.vue'
import WeekView from './views/WeekView.vue'

const props = withDefaults(defineProps<{
	events?: CalendarEvent[]
	startDate?: Date
	view?: CalendarView
	weekStart?: WeekStart
	availabilityMode?: boolean
	availabilityEditable?: boolean
	availabilitySlots?: AvailabilitySlot[]
	viewSwitcher?: 'dropdown' | 'tabs'
}>(), {
	startDate: () => new Date(),
	view: 'Week',
	weekStart: 'Sunday',
	events: () => [],
	availabilityMode: false,
	availabilityEditable: false,
	availabilitySlots: () => [],
	viewSwitcher: 'dropdown',
})

const emit = defineEmits<{
	(e: 'eventClick', event: CalendarEvent): void
	(e: 'eventCreate', event: { start_time: Date, end_time: Date }): void
	(e: 'eventUpdate', event: CalendarEvent): void
	(e: 'eventDelete', eventId: string): void
	(e: 'dateChange', changeEvent: CalendarViewState): void
	(e: 'viewChange', changeEvent: CalendarViewState): void
	(e: 'ready', changeEvent: CalendarViewState): void
	(e: 'availabilityChange', slots: AvailabilitySlot[]): void
}>()

const { t } = useI18n()

const currentDate = ref(new Date(props.startDate))
const currentView = ref(props.view)
const activeEvent = ref<CalendarEvent>()
const showPopover = ref(false)
const popoverPosition = ref<{ top: number, left: number }>()

const views: Record<CalendarView, Component> = {
	Week: WeekView,
	Month: MonthView,
	Day: DayView,
	Agenda: AgendaView
}

const viewTabs = computed(() => (Object.keys(views) as CalendarView[]).map(key => ({
	label: t(`calendar.views.${key.toLowerCase()}`),
	value: key,
})))

const viewToDeltaKey: Record<CalendarView, string> = {
	Week: 'weeks',
	Month: 'months',
	Day: 'days',
	Agenda: 'days'
}

const visibleDateRange = computed(() => {
	const start = new Date(currentDate.value)
	let end = new Date(currentDate.value)
	const addSunday = props.weekStart === 'Sunday' ? 0 : 1
	switch (currentView.value) {
		case 'Week':
			start.setDate(start.getDate() - start.getDay() + addSunday)
			end.setDate(start.getDate() + 6)
			break
		case 'Month':
		case 'Agenda':
			start.setDate(-6)
			end = new Date(start.getFullYear(), start.getMonth() + 2, 7)
			break
		case 'Day':
			end.setDate(end.getDate() + 1)
			break
		default:
			break
	}
	return { start, end }
})

const state = computed<CalendarViewState>(() => ({
	visibleDateRange: visibleDateRange.value,
	currentDate: currentDate.value,
	currentView: currentView.value
}))

function handleDateChange(date: Date) {
	currentDate.value = new Date(date)
	emit('dateChange', state.value)
}

// Agenda view emits dateChange on scroll for header display,
// but we must NOT feed it back into currentDate or it causes a scroll loop.
function handleAgendaDateChange(date: Date) {
	emit('dateChange', { visibleDateRange: visibleDateRange.value, currentDate: date, currentView: currentView.value })
}

function handleViewChange(view: CalendarView) {
	currentView.value = view
	emit('viewChange', state.value)
}

// Event handlers
function handleEventClick(event: CalendarEvent) {
	emit('eventClick', event)
}

function handleEventCreate(event: { start_time: Date, end_time: Date }) {
	emit('eventCreate', event)
}

function handleEventUpdate(event: CalendarEvent) {
	emit('eventUpdate', event)
}

function handleEventDelete(eventId: string) {
	emit('eventDelete', eventId)
}

// Popover handling
function openPopover(event: CalendarEvent, position?: { top: number, left: number }) {
	activeEvent.value = event
	popoverPosition.value = position
	showPopover.value = true
}

function closePopover() {
	showPopover.value = false
	activeEvent.value = undefined
	popoverPosition.value = undefined
}

defineExpose({
	visibleDateRange,
	currentView,
	currentDate,
	openPopover,
	closePopover
})

onMounted(() => {
	emit('ready', state.value)
})
</script>

<template>
	<div class="calendar">
		<div class="flex m_block p-1">
			<h3 class="txt-light my-0 m_pb-05">
				<b>{{ formatDate(currentDate, 'MMMM') }}</b>
				{{ formatDate(currentDate, 'YYYY') }}
			</h3>
			<div class="ms-auto flex gap-025 m_flex-wrap">
				<TabsNav size="sm" v-if="viewSwitcher === 'tabs'" :model-value="currentView" :tabs="viewTabs" group="calendar-view" @update:model-value="handleViewChange($event as CalendarView)"
					style="--bgl_tabs-border-radius: calc(var(--bgl-btn-border-radius) / 2)" class="txt12 m_mb-05 m_w-100p " align-txt="center" />
				<Dropdown v-else thin :value="currentView" iconEnd="keyboard_arrow_down" color="gray">
					<ListItem v-for="(_, key) in views" :key="key" thin :title="t(`calendar.views.${key.toLowerCase()}`)" @click="handleViewChange(key)" />
				</Dropdown>
		<div class="flex gap-025">
			<Btn icon="calendar" size="sm" class="txt12" color="gray" :value="t('calendar.today')" @click="handleDateChange(new Date())" />
			<Btn
					icon="chevron_left" color="gray" size="sm" class=""
					:disabled="currentView === 'Agenda'"
					@click="handleDateChange(timeDelta(currentDate, { [viewToDeltaKey[currentView]]: -1 }))"
				/>
				<Btn
					icon="chevron_right" color="gray" size="sm" class=""
					:disabled="currentView === 'Agenda'"
					@click="handleDateChange(timeDelta(currentDate, { [viewToDeltaKey[currentView]]: 1 }))"
				/>
		</div>
			</div>
		</div>
		<component
			:is="views[currentView]" :events="events" :start-date="currentDate" :week-start="weekStart"
			:availability-mode="props.availabilityMode" :availability-editable="props.availabilityEditable" :availability-slots="props.availabilitySlots"
			@event-click="handleEventClick" @event-create="handleEventCreate" @event-update="handleEventUpdate"
		@event-delete="handleEventDelete" @date-change="currentView === 'Agenda' ? handleAgendaDateChange($event) : handleDateChange($event)" @open-popover="openPopover"
			@availability-change="(slots: AvailabilitySlot[]) => emit('availabilityChange', slots)"
		>
			<template #eventContent="{ event, close }">
				<slot name="eventContent" :event="event" :close="close" />
			</template>
		</component>

		<!-- Global Popover -->
		<CalendarPopover
			:event="activeEvent" :position="popoverPosition" :show="showPopover"
			:has-event-content-slot="!!$slots.eventContent" @close="closePopover" @event-click="handleEventClick"
		>
			<template #eventContent="{ event, close }">
				<slot name="eventContent" :event="event" :close="close" />
			</template>
		</CalendarPopover>
	</div>
</template>

<style scoped>

.calendar {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
}

</style>
