<template>
  <div ref="gridRef" class="calendar-grid">
    <FullCalendar ref="calendarRef" :options="calendarOptions" />
  </div>
</template>

<script setup lang="ts">
import { computed, ref, onMounted, onUnmounted, nextTick } from 'vue'
import FullCalendar from '@fullcalendar/vue3'
import dayGridPlugin from '@fullcalendar/daygrid'
import timeGridPlugin from '@fullcalendar/timegrid'
import interactionPlugin from '@fullcalendar/interaction'
import zhCnLocale from '@fullcalendar/core/locales/zh-cn'
import type {
  CalendarOptions,
  EventInput,
  DateSelectArg,
  EventDropArg,
  EventClickArg,
} from '@fullcalendar/core'
import { useCalendarStore } from '@/stores/calendar'
import type { ICalendarEvent } from '@/api/calendar'

const store = useCalendarStore()

const gridRef = ref<HTMLElement>()
const calendarRef = ref<InstanceType<typeof FullCalendar>>()

let resizeObserver: ResizeObserver | null = null

const emit = defineEmits<{
  (e: 'create', start?: Date, end?: Date): void
  (e: 'edit', event: ICalendarEvent): void
}>()

function toFcEvent(ev: ICalendarEvent): EventInput {
  const eventType = store.eventTypes.find((t) => t.id === ev.event_type_id)
  const color = eventType?.color || '#1677ff'
  return {
    id: ev.id,
    title: ev.title,
    start: ev.start_time,
    end: ev.end_time,
    allDay: ev.is_all_day,
    backgroundColor: color,
    borderColor: color,
    classNames: ev.is_invited ? ['fc-event-invited'] : [],
    extendedProps: { event: ev },
  }
}

function handleSelect(arg: DateSelectArg): void {
  emit('create', arg.start, arg.end)
}

async function handleEventDrop(arg: EventDropArg): Promise<void> {
  const ev = arg.event.extendedProps.event as ICalendarEvent
  await store.updateEvent(ev.id, {
    start_time: arg.event.start?.toISOString() ?? ev.start_time,
    end_time: arg.event.end?.toISOString() ?? ev.end_time,
  })
}

function handleEventClick(arg: EventClickArg): void {
  const ev = arg.event.extendedProps.event as ICalendarEvent
  emit('edit', ev)
}

onMounted(() => {
  // ponytail: track last size to break ResizeObserver feedback loop —
  // updateSize() can trigger another resize event, causing infinite thrash.
  let lastW = 0
  let lastH = 0
  resizeObserver = new ResizeObserver((entries) => {
    const e = entries[0]
    const w = e.contentRect.width
    const h = e.contentRect.height
    if (w === lastW && h === lastH) return
    lastW = w
    lastH = h
    nextTick(() => {
      calendarRef.value?.getApi()?.updateSize()
    })
  })
  if (gridRef.value) {
    resizeObserver.observe(gridRef.value)
  }
})

onUnmounted(() => {
  resizeObserver?.disconnect()
})

const calendarOptions = computed<CalendarOptions>(() => ({
  plugins: [dayGridPlugin, timeGridPlugin, interactionPlugin],
  locale: zhCnLocale,
  initialView: 'dayGridMonth',
  firstDay: 1,
  headerToolbar: {
    left: 'prev,next today',
    center: 'title',
    right: 'dayGridMonth,timeGridWeek,timeGridDay',
  },
  buttonText: {
    today: '今天',
    month: '月',
    week: '周',
    day: '日',
  },
  selectable: true,
  editable: true,
  height: '100%',
  events: store.events.map(toFcEvent),
  select: handleSelect,
  eventDrop: handleEventDrop,
  eventClick: handleEventClick,
}))
</script>

<style scoped>
.calendar-grid {
  flex: 1;
  min-height: 0;
  overflow: hidden;
}

.calendar-grid :deep(.fc) {
  height: 100%;
}

.calendar-grid :deep(.fc-event-invited) {
  border-style: dashed !important;
  border-width: 2px !important;
  opacity: 0.75;
}
</style>
