<template>
  <div ref="gridRef" class="calendar-grid">
    <div
      v-if="store.isLoading && store.events.length === 0"
      class="calendar-grid__state calendar-grid__skeleton"
    >
      <div v-for="i in 6" :key="i" class="calendar-grid__skeleton-row" />
    </div>
    <div
      v-else-if="store.error && store.events.length === 0"
      class="calendar-grid__state calendar-grid__error"
    >
      <WarningOutlined class="calendar-grid__error-icon" />
      <span class="calendar-grid__error-text">{{ store.error }}</span>
      <a-button size="small" type="primary" @click="retry">重试</a-button>
    </div>
    <template v-else>
      <FullCalendar ref="calendarRef" :options="calendarOptions" />
      <div v-if="store.events.length === 0" class="calendar-grid__empty-hint">
        暂无日程
      </div>
    </template>
  </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 { WarningOutlined } from '@ant-design/icons-vue'
import type {
  CalendarOptions,
  EventInput,
  DateSelectArg,
  EventDropArg,
  EventClickArg,
} from '@fullcalendar/core'
import { useCalendarStore } from '@/stores/calendar'
import type { ICalendarEvent } from '@/api/calendar'
import { eventColorToken } from '@/utils/calendarTokens'

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 = eventColorToken(eventType?.name)
  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)
}

function retry(): void {
  void store.loadEvents()
}

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;
  position: relative;
}

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

.calendar-grid__state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  height: 100%;
  padding: var(--space-8);
}

.calendar-grid__skeleton {
  gap: var(--space-2);
  width: 100%;
  max-width: 480px;
  margin: 0 auto;
}

.calendar-grid__skeleton-row {
  width: 100%;
  height: 28px;
  border-radius: var(--radius-md);
  background: linear-gradient(
    90deg,
    var(--border-color) 25%,
    var(--bg-tertiary) 37%,
    var(--border-color) 63%
  );
  background-size: 400% 100%;
  animation: calendar-grid-shimmer 1.4s ease infinite;
}

@keyframes calendar-grid-shimmer {
  0% {
    background-position: 100% 50%;
  }
  100% {
    background-position: 0 50%;
  }
}

.calendar-grid__error {
  color: var(--color-error);
}

.calendar-grid__error-icon {
  font-size: 24px;
}

.calendar-grid__error-text {
  font-size: var(--font-sm);
  color: var(--text-tertiary);
  text-align: center;
}

.calendar-grid__empty-hint {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  color: var(--text-tertiary);
  font-size: var(--font-sm);
  pointer-events: none;
}
</style>
