import { useState, useCallback, useEffect } from 'react';

// src/calendar/types/index.ts
var RecurrenceType = /* @__PURE__ */ ((RecurrenceType2) => {
  RecurrenceType2["DAILY"] = "daily";
  RecurrenceType2["WEEKLY"] = "weekly";
  RecurrenceType2["MONTHLY"] = "monthly";
  RecurrenceType2["YEARLY"] = "yearly";
  RecurrenceType2["CUSTOM"] = "custom";
  return RecurrenceType2;
})(RecurrenceType || {});
var ReminderType = /* @__PURE__ */ ((ReminderType2) => {
  ReminderType2["NOTIFICATION"] = "notification";
  ReminderType2["EMAIL"] = "email";
  ReminderType2["SMS"] = "sms";
  return ReminderType2;
})(ReminderType || {});
var ReminderStatus = /* @__PURE__ */ ((ReminderStatus2) => {
  ReminderStatus2["PENDING"] = "pending";
  ReminderStatus2["SENT"] = "sent";
  ReminderStatus2["FAILED"] = "failed";
  return ReminderStatus2;
})(ReminderStatus || {});
var CalendarViewType = /* @__PURE__ */ ((CalendarViewType2) => {
  CalendarViewType2["MONTH"] = "month";
  CalendarViewType2["WEEK"] = "week";
  CalendarViewType2["DAY"] = "day";
  CalendarViewType2["AGENDA"] = "agenda";
  return CalendarViewType2;
})(CalendarViewType || {});
var EventColor = /* @__PURE__ */ ((EventColor2) => {
  EventColor2["BLUE"] = "#3B82F6";
  EventColor2["GREEN"] = "#10B981";
  EventColor2["PURPLE"] = "#8B5CF6";
  EventColor2["RED"] = "#EF4444";
  EventColor2["YELLOW"] = "#F59E0B";
  EventColor2["PINK"] = "#EC4899";
  EventColor2["INDIGO"] = "#6366F1";
  EventColor2["GRAY"] = "#6B7280";
  return EventColor2;
})(EventColor || {});
var EventPriority = /* @__PURE__ */ ((EventPriority2) => {
  EventPriority2["LOW"] = "low";
  EventPriority2["NORMAL"] = "normal";
  EventPriority2["HIGH"] = "high";
  EventPriority2["URGENT"] = "urgent";
  return EventPriority2;
})(EventPriority || {});
var EventSortField = /* @__PURE__ */ ((EventSortField2) => {
  EventSortField2["START_TIME"] = "startTime";
  EventSortField2["TITLE"] = "title";
  EventSortField2["PRIORITY"] = "priority";
  EventSortField2["CREATED_AT"] = "createdAt";
  EventSortField2["UPDATED_AT"] = "updatedAt";
  return EventSortField2;
})(EventSortField || {});
var SortDirection = /* @__PURE__ */ ((SortDirection2) => {
  SortDirection2["ASC"] = "asc";
  SortDirection2["DESC"] = "desc";
  return SortDirection2;
})(SortDirection || {});
var EventListDisplayMode = /* @__PURE__ */ ((EventListDisplayMode2) => {
  EventListDisplayMode2["LIST"] = "list";
  EventListDisplayMode2["GRID"] = "grid";
  return EventListDisplayMode2;
})(EventListDisplayMode || {});

// src/calendar/utils/dateUtils.ts
function formatDate(date) {
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, "0");
  const day = String(date.getDate()).padStart(2, "0");
  return year + "-" + month + "-" + day;
}
function formatTime(date) {
  return date.toLocaleTimeString("zh-CN", {
    hour: "2-digit",
    minute: "2-digit",
    hour12: false
  });
}
function formatDateTime(date, options) {
  const defaultOptions = {
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
    hour: "2-digit",
    minute: "2-digit",
    hour12: false
  };
  return date.toLocaleString("zh-CN", { ...defaultOptions, ...options });
}
function toLocalISOString(date) {
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, "0");
  const day = String(date.getDate()).padStart(2, "0");
  const hours = String(date.getHours()).padStart(2, "0");
  const minutes = String(date.getMinutes()).padStart(2, "0");
  const seconds = String(date.getSeconds()).padStart(2, "0");
  const milliseconds = String(date.getMilliseconds()).padStart(3, "0");
  return year + "-" + month + "-" + day + "T" + hours + ":" + minutes + ":" + seconds + "." + milliseconds;
}
function getMonthStart(date) {
  return new Date(date.getFullYear(), date.getMonth(), 1);
}
function getMonthEnd(date) {
  return new Date(date.getFullYear(), date.getMonth() + 1, 0);
}
function getWeekStart(date, firstDayOfWeek = 1) {
  const day = date.getDay();
  const diff = day - firstDayOfWeek;
  const adjustedDiff = diff < 0 ? diff + 7 : diff;
  const weekStart = new Date(date);
  weekStart.setDate(date.getDate() - adjustedDiff);
  weekStart.setHours(0, 0, 0, 0);
  return weekStart;
}
function getWeekEnd(date, firstDayOfWeek = 1) {
  const weekStart = getWeekStart(date, firstDayOfWeek);
  const weekEnd = new Date(weekStart);
  weekEnd.setDate(weekStart.getDate() + 6);
  weekEnd.setHours(23, 59, 59, 999);
  return weekEnd;
}
function getDayStart(date) {
  const dayStart = new Date(date);
  dayStart.setHours(0, 0, 0, 0);
  return dayStart;
}
function getDayEnd(date) {
  const dayEnd = new Date(date);
  dayEnd.setHours(23, 59, 59, 999);
  return dayEnd;
}
function isSameDay(date1, date2) {
  return date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() === date2.getDate();
}
function isSameWeek(date1, date2, firstDayOfWeek = 1) {
  const week1Start = getWeekStart(date1, firstDayOfWeek);
  const week2Start = getWeekStart(date2, firstDayOfWeek);
  return isSameDay(week1Start, week2Start);
}
function isSameMonth(date1, date2) {
  return date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth();
}
function isToday(date) {
  return isSameDay(date, /* @__PURE__ */ new Date());
}
function isWeekend(date) {
  const day = date.getDay();
  return day === 0 || day === 6;
}
function addDays(date, days) {
  const result = new Date(date);
  result.setDate(result.getDate() + days);
  return result;
}
function addWeeks(date, weeks) {
  return addDays(date, weeks * 7);
}
function addMonths(date, months) {
  const result = new Date(date);
  result.setMonth(result.getMonth() + months);
  return result;
}
function addYears(date, years) {
  const result = new Date(date);
  result.setFullYear(result.getFullYear() + years);
  return result;
}
function getMonthViewDates(date, firstDayOfWeek = 1) {
  const monthStart = getMonthStart(date);
  getMonthEnd(date);
  const viewStart = getWeekStart(monthStart, firstDayOfWeek);
  const dates = [];
  let currentDate = new Date(viewStart);
  for (let i = 0; i < 42; i++) {
    dates.push(new Date(currentDate));
    currentDate.setDate(currentDate.getDate() + 1);
  }
  return dates;
}
function getWeekViewDates(date, firstDayOfWeek = 1) {
  const weekStart = getWeekStart(date, firstDayOfWeek);
  const dates = [];
  for (let i = 0; i < 7; i++) {
    const currentDate = new Date(weekStart);
    currentDate.setDate(weekStart.getDate() + i);
    dates.push(currentDate);
  }
  return dates;
}
function generateTimeSlots(startHour = 0, endHour = 24, interval = 60) {
  const today = /* @__PURE__ */ new Date();
  const slots = [];
  for (let hour = startHour; hour < endHour; hour++) {
    for (let minute = 0; minute < 60; minute += interval) {
      const slot = new Date(today);
      slot.setHours(hour, minute, 0, 0);
      slots.push(slot);
    }
  }
  return slots;
}
function parseDate(dateString) {
  if (dateString.includes("T")) {
    return new Date(dateString);
  } else if (dateString.includes("-")) {
    const parts = dateString.split("-").map(Number);
    const year = parts[0];
    const month = parts[1];
    const day = parts[2];
    if (year !== void 0 && month !== void 0 && day !== void 0) {
      return new Date(year, month - 1, day);
    }
    return new Date(dateString);
  } else {
    return new Date(dateString);
  }
}
function getDaysDifference(date1, date2) {
  const timeDiff = Math.abs(date2.getTime() - date1.getTime());
  return Math.ceil(timeDiff / (1e3 * 60 * 60 * 24));
}
function isWorkingHour(date, workingStart = "09:00", workingEnd = "18:00") {
  const timeString = formatTime(date);
  return timeString >= workingStart && timeString <= workingEnd;
}
function getMonthName(date, locale = "zh-CN") {
  return date.toLocaleDateString(locale, { month: "long" });
}
function getWeekdayName(date, locale = "zh-CN", format = "long") {
  return date.toLocaleDateString(locale, { weekday: format });
}
function getRelativeTime(date, locale = "zh-CN") {
  const now = /* @__PURE__ */ new Date();
  const diff = date.getTime() - now.getTime();
  const diffInDays = Math.floor(diff / (1e3 * 60 * 60 * 24));
  if (diffInDays === 0) {
    if (isSameDay(date, now)) {
      return "\u4ECA\u5929";
    }
  } else if (diffInDays === 1) {
    return "\u660E\u5929";
  } else if (diffInDays === -1) {
    return "\u6628\u5929";
  } else if (diffInDays > 1 && diffInDays <= 7) {
    return diffInDays + "\u5929\u540E";
  } else if (diffInDays < -1 && diffInDays >= -7) {
    return Math.abs(diffInDays) + "\u5929\u524D";
  }
  return formatDate(date);
}
function isValidDate(date) {
  return date instanceof Date && !isNaN(date.getTime());
}
function cloneDate(date) {
  return new Date(date.getTime());
}

// src/calendar/hooks/useEvents.ts
function useEvents() {
  const [events, setEvents] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState();
  const clearError = useCallback(() => {
    setError(void 0);
  }, []);
  const fetchEvents = useCallback(async (startDate, endDate) => {
    setLoading(true);
    setError(void 0);
    try {
      const params = new URLSearchParams({
        startDate: toLocalISOString(startDate),
        endDate: toLocalISOString(endDate)
      });
      const response = await fetch("/api/calendar/events?" + params);
      if (!response.ok) {
        throw new Error("\u83B7\u53D6\u4E8B\u4EF6\u5931\u8D25: " + response.status);
      }
      const data = await response.json();
      if (!data.success) {
        throw new Error(data.error || "\u83B7\u53D6\u4E8B\u4EF6\u5931\u8D25");
      }
      const eventsWithDates = data.data.map((event) => ({
        ...event,
        startTime: new Date(event.startTime),
        endTime: new Date(event.endTime),
        createdAt: new Date(event.createdAt),
        updatedAt: new Date(event.updatedAt)
      }));
      setEvents(eventsWithDates);
    } catch (err) {
      console.error("\u83B7\u53D6\u4E8B\u4EF6\u5931\u8D25:", err);
      setError(err instanceof Error ? err.message : "\u83B7\u53D6\u4E8B\u4EF6\u5931\u8D25");
    } finally {
      setLoading(false);
    }
  }, []);
  const createEvent = useCallback(async (eventData) => {
    setLoading(true);
    setError(void 0);
    try {
      const createRequest = {
        title: eventData.title,
        description: eventData.description,
        startTime: toLocalISOString(eventData.startTime),
        endTime: toLocalISOString(eventData.endTime),
        allDay: eventData.allDay,
        location: eventData.location,
        color: eventData.color,
        priority: eventData.priority,
        // TODO: 支持重复规则和提醒
        recurrence: eventData.recurrence,
        reminders: eventData.reminders?.map((reminder) => ({
          reminderTime: toLocalISOString(reminder.reminderTime),
          reminderType: reminder.reminderType
        }))
      };
      const response = await fetch("/api/calendar/events", {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify(createRequest)
      });
      if (!response.ok) {
        throw new Error("\u521B\u5EFA\u4E8B\u4EF6\u5931\u8D25: " + response.status);
      }
      const data = await response.json();
      if (!data.success) {
        throw new Error(data.error || "\u521B\u5EFA\u4E8B\u4EF6\u5931\u8D25");
      }
      const newEvent = {
        ...data.data,
        startTime: new Date(data.data.startTime),
        endTime: new Date(data.data.endTime),
        createdAt: new Date(data.data.createdAt),
        updatedAt: new Date(data.data.updatedAt)
      };
      setEvents((prev) => [...prev, newEvent]);
      return newEvent;
    } catch (err) {
      console.error("\u521B\u5EFA\u4E8B\u4EF6\u5931\u8D25:", err);
      const errorMessage = err instanceof Error ? err.message : "\u521B\u5EFA\u4E8B\u4EF6\u5931\u8D25";
      setError(errorMessage);
      throw new Error(errorMessage);
    } finally {
      setLoading(false);
    }
  }, []);
  const updateEvent = useCallback(async (eventId, eventData) => {
    setLoading(true);
    setError(void 0);
    try {
      const updateRequest = {};
      if (eventData.title !== void 0) updateRequest.title = eventData.title;
      if (eventData.description !== void 0) updateRequest.description = eventData.description;
      if (eventData.startTime !== void 0) updateRequest.startTime = toLocalISOString(eventData.startTime);
      if (eventData.endTime !== void 0) updateRequest.endTime = toLocalISOString(eventData.endTime);
      if (eventData.allDay !== void 0) updateRequest.allDay = eventData.allDay;
      if (eventData.location !== void 0) updateRequest.location = eventData.location;
      if (eventData.color !== void 0) updateRequest.color = eventData.color;
      const response = await fetch("/api/calendar/events/" + eventId, {
        method: "PUT",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify(updateRequest)
      });
      if (!response.ok) {
        throw new Error("\u66F4\u65B0\u4E8B\u4EF6\u5931\u8D25: " + response.status);
      }
      const data = await response.json();
      if (!data.success) {
        throw new Error(data.error || "\u66F4\u65B0\u4E8B\u4EF6\u5931\u8D25");
      }
      const updatedEvent = {
        ...data.data,
        startTime: new Date(data.data.startTime),
        endTime: new Date(data.data.endTime),
        createdAt: new Date(data.data.createdAt),
        updatedAt: new Date(data.data.updatedAt)
      };
      setEvents((prev) => prev.map(
        (event) => event.id === eventId ? updatedEvent : event
      ));
      return updatedEvent;
    } catch (err) {
      console.error("\u66F4\u65B0\u4E8B\u4EF6\u5931\u8D25:", err);
      const errorMessage = err instanceof Error ? err.message : "\u66F4\u65B0\u4E8B\u4EF6\u5931\u8D25";
      setError(errorMessage);
      throw new Error(errorMessage);
    } finally {
      setLoading(false);
    }
  }, []);
  const deleteEvent = useCallback(async (eventId, deleteAll = false) => {
    setLoading(true);
    setError(void 0);
    try {
      const url = deleteAll ? "/api/calendar/events/" + eventId + "?deleteAll=true" : "/api/calendar/events/" + eventId;
      const response = await fetch(url, {
        method: "DELETE"
      });
      if (!response.ok) {
        throw new Error("\u5220\u9664\u4E8B\u4EF6\u5931\u8D25: " + response.status);
      }
      const data = await response.json();
      if (!data.success) {
        throw new Error(data.error || "\u5220\u9664\u4E8B\u4EF6\u5931\u8D25");
      }
      setEvents((prev) => prev.filter((event) => event.id !== eventId));
    } catch (err) {
      console.error("\u5220\u9664\u4E8B\u4EF6\u5931\u8D25:", err);
      const errorMessage = err instanceof Error ? err.message : "\u5220\u9664\u4E8B\u4EF6\u5931\u8D25";
      setError(errorMessage);
      throw new Error(errorMessage);
    } finally {
      setLoading(false);
    }
  }, []);
  const batchDeleteEvents = useCallback(async (eventIds) => {
    setLoading(true);
    setError(void 0);
    try {
      const response = await fetch("/api/calendar/events/batchDelete", {
        method: "DELETE",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify({ eventIds })
      });
      if (!response.ok) {
        throw new Error("\u6279\u91CF\u5220\u9664\u4E8B\u4EF6\u5931\u8D25: " + response.status);
      }
      const data = await response.json();
      if (!data.success) {
        throw new Error(data.error || "\u6279\u91CF\u5220\u9664\u4E8B\u4EF6\u5931\u8D25");
      }
      setEvents((prev) => prev.filter((event) => !eventIds.includes(event.id)));
    } catch (err) {
      console.error("\u6279\u91CF\u5220\u9664\u4E8B\u4EF6\u5931\u8D25:", err);
      const errorMessage = err instanceof Error ? err.message : "\u6279\u91CF\u5220\u9664\u4E8B\u4EF6\u5931\u8D25";
      setError(errorMessage);
      throw new Error(errorMessage);
    } finally {
      setLoading(false);
    }
  }, []);
  return {
    events,
    loading,
    error,
    createEvent,
    updateEvent,
    deleteEvent,
    batchDeleteEvents,
    fetchEvents,
    clearError
  };
}

// src/calendar/services/eventTypeService.ts
var EventType = /* @__PURE__ */ ((EventType2) => {
  EventType2["SINGLE"] = "single";
  EventType2["MULTI_DAY"] = "multi_day";
  EventType2["RECURRING"] = "recurring";
  return EventType2;
})(EventType || {});
var RecurrencePattern = /* @__PURE__ */ ((RecurrencePattern2) => {
  RecurrencePattern2["DAILY"] = "daily";
  RecurrencePattern2["WEEKLY"] = "weekly";
  RecurrencePattern2["MONTHLY"] = "monthly";
  RecurrencePattern2["YEARLY"] = "yearly";
  RecurrencePattern2["CUSTOM"] = "custom";
  return RecurrencePattern2;
})(RecurrencePattern || {});
var EventTypeService = class {
  /**
   * 根据事件数据生成实际的事件实例
   */
  static generateEventInstances(eventData, viewStartDate, viewEndDate, parentEventId) {
    switch (eventData.type) {
      case "single" /* SINGLE */:
        return this.generateSingleEvent(eventData, parentEventId);
      case "multi_day" /* MULTI_DAY */:
        return this.generateMultiDayEvents(eventData, viewStartDate, viewEndDate, parentEventId);
      case "recurring" /* RECURRING */:
        return this.generateRecurringEvents(eventData, viewStartDate, viewEndDate, parentEventId);
      default:
        throw new Error("\u4E0D\u652F\u6301\u7684\u4E8B\u4EF6\u7C7B\u578B: " + eventData.type);
    }
  }
  /**
   * 生成单次事件
   */
  static generateSingleEvent(eventData, parentEventId) {
    return [{
      ...eventData,
      eventType: "single" /* SINGLE */,
      parentEventId
    }];
  }
  /**
   * 生成多天事件实例
   * 例：21-23号的会议 -> 创建3个事件实例，每个代表一天
   */
  static generateMultiDayEvents(eventData, viewStartDate, viewEndDate, parentEventId) {
    const events = [];
    const currentDate = new Date(eventData.startDate);
    const actualStartDate = currentDate < viewStartDate ? viewStartDate : currentDate;
    const actualEndDate = eventData.endDate > viewEndDate ? viewEndDate : eventData.endDate;
    currentDate.setTime(actualStartDate.getTime());
    while (currentDate <= actualEndDate) {
      const dayStart = new Date(currentDate);
      const dayEnd = new Date(currentDate);
      if (eventData.allDay) {
        dayStart.setHours(0, 0, 0, 0);
        dayEnd.setHours(23, 59, 59, 999);
      } else {
        const startParts = (eventData.startTime || "09:00").split(":");
        const endParts = (eventData.endTime || "17:00").split(":");
        const startHour = Number(startParts[0] || 0);
        const startMinute = Number(startParts[1] || 0);
        const endHour = Number(endParts[0] || 0);
        const endMinute = Number(endParts[1] || 0);
        dayStart.setHours(startHour, startMinute, 0, 0);
        dayEnd.setHours(endHour, endMinute, 0, 0);
      }
      events.push({
        title: eventData.title,
        description: eventData.description,
        startTime: dayStart,
        endTime: dayEnd,
        location: eventData.location,
        color: eventData.color,
        priority: eventData.priority,
        allDay: eventData.allDay,
        eventType: "multi_day" /* MULTI_DAY */,
        parentEventId,
        instanceDate: this.formatDateString(currentDate),
        isMultiDayPart: true
      });
      currentDate.setDate(currentDate.getDate() + 1);
    }
    return events;
  }
  /**
   * 生成重复事件实例
   */
  static generateRecurringEvents(eventData, viewStartDate, viewEndDate, parentEventId) {
    const events = [];
    const { recurrence } = eventData;
    let currentDate = new Date(eventData.startDate);
    let instanceCount = 0;
    const maxInstances = recurrence.count || 1e3;
    const endDate = recurrence.endDate || viewEndDate;
    const actualEndDate = endDate < viewEndDate ? endDate : viewEndDate;
    while (currentDate <= actualEndDate && instanceCount < maxInstances) {
      if (currentDate >= viewStartDate) {
        const instanceStart = new Date(currentDate);
        instanceStart.setHours(
          eventData.startTime.getHours(),
          eventData.startTime.getMinutes(),
          eventData.startTime.getSeconds()
        );
        const instanceEnd = new Date(currentDate);
        instanceEnd.setHours(
          eventData.endTime.getHours(),
          eventData.endTime.getMinutes(),
          eventData.endTime.getSeconds()
        );
        events.push({
          title: eventData.title,
          description: eventData.description,
          startTime: instanceStart,
          endTime: instanceEnd,
          location: eventData.location,
          color: eventData.color,
          priority: eventData.priority,
          allDay: eventData.allDay,
          eventType: "recurring" /* RECURRING */,
          parentEventId,
          instanceDate: this.formatDateString(currentDate),
          isRecurringInstance: true
        });
        instanceCount++;
      }
      currentDate = this.getNextRecurrenceDate(currentDate, recurrence);
    }
    return events;
  }
  /**
   * 计算下一次重复的日期
   */
  static getNextRecurrenceDate(currentDate, recurrence) {
    const nextDate = new Date(currentDate);
    switch (recurrence.pattern) {
      case "daily" /* DAILY */:
        nextDate.setDate(nextDate.getDate() + recurrence.interval);
        break;
      case "weekly" /* WEEKLY */:
        nextDate.setDate(nextDate.getDate() + 7 * recurrence.interval);
        break;
      case "monthly" /* MONTHLY */:
        nextDate.setMonth(nextDate.getMonth() + recurrence.interval);
        break;
      case "yearly" /* YEARLY */:
        nextDate.setFullYear(nextDate.getFullYear() + recurrence.interval);
        break;
      default:
        throw new Error("\u4E0D\u652F\u6301\u7684\u91CD\u590D\u6A21\u5F0F: " + recurrence.pattern);
    }
    return nextDate;
  }
  /**
   * 格式化日期为字符串 (YYYY-MM-DD)
   */
  static formatDateString(date) {
    return date.toISOString().split("T")[0];
  }
  /**
   * 验证事件数据
   */
  static validateEventData(eventData) {
    const errors = [];
    if (!eventData.title?.trim()) {
      errors.push("\u4E8B\u4EF6\u6807\u9898\u4E0D\u80FD\u4E3A\u7A7A");
    }
    if (!eventData.color) {
      errors.push("\u8BF7\u9009\u62E9\u4E8B\u4EF6\u989C\u8272");
    }
    switch (eventData.type) {
      case "single" /* SINGLE */:
        errors.push(...this.validateSingleEvent(eventData));
        break;
      case "multi_day" /* MULTI_DAY */:
        errors.push(...this.validateMultiDayEvent(eventData));
        break;
      case "recurring" /* RECURRING */:
        errors.push(...this.validateRecurringEvent(eventData));
        break;
    }
    return errors;
  }
  static validateSingleEvent(eventData) {
    const errors = [];
    if (!eventData.startTime) {
      errors.push("\u8BF7\u8BBE\u7F6E\u5F00\u59CB\u65F6\u95F4");
    }
    if (!eventData.endTime) {
      errors.push("\u8BF7\u8BBE\u7F6E\u7ED3\u675F\u65F6\u95F4");
    }
    if (eventData.startTime && eventData.endTime && eventData.endTime <= eventData.startTime) {
      errors.push("\u7ED3\u675F\u65F6\u95F4\u5FC5\u987B\u665A\u4E8E\u5F00\u59CB\u65F6\u95F4");
    }
    return errors;
  }
  static validateMultiDayEvent(eventData) {
    const errors = [];
    if (!eventData.startDate) {
      errors.push("\u8BF7\u8BBE\u7F6E\u5F00\u59CB\u65E5\u671F");
    }
    if (!eventData.endDate) {
      errors.push("\u8BF7\u8BBE\u7F6E\u7ED3\u675F\u65E5\u671F");
    }
    if (eventData.startDate && eventData.endDate && eventData.endDate < eventData.startDate) {
      errors.push("\u7ED3\u675F\u65E5\u671F\u5FC5\u987B\u4E0D\u65E9\u4E8E\u5F00\u59CB\u65E5\u671F");
    }
    if (!eventData.allDay) {
      if (eventData.startTime && !/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/.test(eventData.startTime)) {
        errors.push("\u5F00\u59CB\u65F6\u95F4\u683C\u5F0F\u4E0D\u6B63\u786E (HH:MM)");
      }
      if (eventData.endTime && !/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/.test(eventData.endTime)) {
        errors.push("\u7ED3\u675F\u65F6\u95F4\u683C\u5F0F\u4E0D\u6B63\u786E (HH:MM)");
      }
    }
    return errors;
  }
  static validateRecurringEvent(eventData) {
    const errors = [];
    if (!eventData.startDate) {
      errors.push("\u8BF7\u8BBE\u7F6E\u5F00\u59CB\u65E5\u671F");
    }
    if (!eventData.startTime) {
      errors.push("\u8BF7\u8BBE\u7F6E\u5F00\u59CB\u65F6\u95F4");
    }
    if (!eventData.endTime) {
      errors.push("\u8BF7\u8BBE\u7F6E\u7ED3\u675F\u65F6\u95F4");
    }
    if (eventData.startTime && eventData.endTime && eventData.endTime <= eventData.startTime) {
      errors.push("\u7ED3\u675F\u65F6\u95F4\u5FC5\u987B\u665A\u4E8E\u5F00\u59CB\u65F6\u95F4");
    }
    const { recurrence } = eventData;
    if (!recurrence.pattern) {
      errors.push("\u8BF7\u9009\u62E9\u91CD\u590D\u6A21\u5F0F");
    }
    if (!recurrence.interval || recurrence.interval < 1) {
      errors.push("\u91CD\u590D\u95F4\u9694\u5FC5\u987B\u5927\u4E8E0");
    }
    if (recurrence.interval > 365) {
      errors.push("\u91CD\u590D\u95F4\u9694\u4E0D\u80FD\u8D85\u8FC7365");
    }
    if (recurrence.endDate && recurrence.count && recurrence.count > 0) {
      errors.push("\u4E0D\u80FD\u540C\u65F6\u8BBE\u7F6E\u7ED3\u675F\u65E5\u671F\u548C\u91CD\u590D\u6B21\u6570");
    }
    if (recurrence.count && (recurrence.count < 1 || recurrence.count > 999)) {
      errors.push("\u91CD\u590D\u6B21\u6570\u5FC5\u987B\u57281-999\u4E4B\u95F4");
    }
    return errors;
  }
  /**
   * 获取事件类型的描述文本
   */
  static getEventTypeDescription(eventData) {
    switch (eventData.type) {
      case "single" /* SINGLE */:
        return "\u5355\u6B21\u4E8B\u4EF6";
      case "multi_day" /* MULTI_DAY */:
        const dayCount = Math.ceil((eventData.endDate.getTime() - eventData.startDate.getTime()) / (24 * 60 * 60 * 1e3)) + 1;
        return "\u6301\u7EED " + dayCount + " \u5929";
      case "recurring" /* RECURRING */:
        const { pattern, interval, count, endDate } = eventData.recurrence;
        let desc = "";
        switch (pattern) {
          case "daily" /* DAILY */:
            desc = interval === 1 ? "\u6BCF\u5929" : "\u6BCF " + interval + " \u5929";
            break;
          case "weekly" /* WEEKLY */:
            desc = interval === 1 ? "\u6BCF\u5468" : "\u6BCF " + interval + " \u5468";
            break;
          case "monthly" /* MONTHLY */:
            desc = interval === 1 ? "\u6BCF\u6708" : "\u6BCF " + interval + " \u4E2A\u6708";
            break;
          case "yearly" /* YEARLY */:
            desc = interval === 1 ? "\u6BCF\u5E74" : "\u6BCF " + interval + " \u5E74";
            break;
        }
        if (count) {
          desc += "\uFF0C\u5171 " + count + " \u6B21";
        } else if (endDate) {
          desc += "\uFF0C\u76F4\u5230 " + endDate.toLocaleDateString("zh-CN");
        }
        return desc;
      default:
        return "\u672A\u77E5\u7C7B\u578B";
    }
  }
};

// src/calendar/hooks/useEnhancedEvents.ts
function useEnhancedEvents() {
  const [events, setEvents] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState();
  const clearError = useCallback(() => {
    setError(void 0);
  }, []);
  const fetchEvents = useCallback(async (startDate, endDate) => {
    setLoading(true);
    setError(void 0);
    try {
      const params = new URLSearchParams({
        startDate: toLocalISOString(startDate),
        endDate: toLocalISOString(endDate)
      });
      const response = await fetch("/api/calendar/events?" + params);
      if (!response.ok) {
        throw new Error("\u83B7\u53D6\u4E8B\u4EF6\u5931\u8D25: " + response.status);
      }
      const data = await response.json();
      if (!data.success) {
        throw new Error(data.error || "\u83B7\u53D6\u4E8B\u4EF6\u5931\u8D25");
      }
      const eventsWithDates = data.data.map((event) => ({
        ...event,
        startTime: new Date(event.startTime),
        endTime: new Date(event.endTime),
        createdAt: new Date(event.createdAt),
        updatedAt: new Date(event.updatedAt)
      }));
      console.log("\u{1F4E5} \u83B7\u53D6\u5230\u7684\u4E8B\u4EF6\u6570\u636E:", {
        requestRange: formatDate(startDate) + " \u5230 " + formatDate(endDate),
        eventCount: eventsWithDates.length,
        eventIds: eventsWithDates.map((e) => e.id)
      });
      setEvents((prev) => {
        const eventsOutsideRange = prev.filter((event) => {
          const eventDate = new Date(event.startTime);
          return eventDate < startDate || eventDate > endDate;
        });
        console.log("\u{1F504} \u4E8B\u4EF6\u5408\u5E76\u7B56\u7565:", {
          previousEventCount: prev.length,
          eventsOutsideRange: eventsOutsideRange.length,
          newEventsInRange: eventsWithDates.length,
          finalEventCount: eventsOutsideRange.length + eventsWithDates.length
        });
        return [...eventsOutsideRange, ...eventsWithDates];
      });
    } catch (err) {
      console.error("\u83B7\u53D6\u4E8B\u4EF6\u5931\u8D25:", err);
      setError(err instanceof Error ? err.message : "\u83B7\u53D6\u4E8B\u4EF6\u5931\u8D25");
    } finally {
      setLoading(false);
    }
  }, []);
  const createEvent = useCallback(async (eventData) => {
    setLoading(true);
    setError(void 0);
    try {
      const createRequest = {
        title: eventData.title,
        description: eventData.description,
        startTime: toLocalISOString(eventData.startTime),
        endTime: toLocalISOString(eventData.endTime),
        allDay: eventData.allDay,
        location: eventData.location,
        color: eventData.color,
        priority: eventData.priority,
        recurrence: eventData.recurrence,
        reminders: eventData.reminders?.map((reminder) => ({
          reminderTime: toLocalISOString(reminder.reminderTime),
          reminderType: reminder.reminderType
        }))
      };
      const response = await fetch("/api/calendar/events", {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify(createRequest)
      });
      if (!response.ok) {
        throw new Error("\u521B\u5EFA\u4E8B\u4EF6\u5931\u8D25: " + response.status);
      }
      const data = await response.json();
      if (!data.success) {
        throw new Error(data.error || "\u521B\u5EFA\u4E8B\u4EF6\u5931\u8D25");
      }
      const newEvent = {
        ...data.data,
        startTime: new Date(data.data.startTime),
        endTime: new Date(data.data.endTime),
        createdAt: new Date(data.data.createdAt),
        updatedAt: new Date(data.data.updatedAt)
      };
      setEvents((prev) => [...prev, newEvent]);
      return newEvent;
    } catch (err) {
      console.error("\u521B\u5EFA\u4E8B\u4EF6\u5931\u8D25:", err);
      const errorMessage = err instanceof Error ? err.message : "\u521B\u5EFA\u4E8B\u4EF6\u5931\u8D25";
      setError(errorMessage);
      throw new Error(errorMessage);
    } finally {
      setLoading(false);
    }
  }, []);
  const mapPriorityToEventPriority = (priority) => {
    switch (priority.toLowerCase()) {
      case "low":
        return "low" /* LOW */;
      case "normal":
        return "normal" /* NORMAL */;
      case "high":
        return "high" /* HIGH */;
      case "urgent":
        return "urgent" /* URGENT */;
      default:
        return "normal" /* NORMAL */;
    }
  };
  const mapPatternToRecurrenceType = (pattern) => {
    switch (pattern) {
      case "daily":
        return "daily" /* DAILY */;
      case "weekly":
        return "weekly" /* WEEKLY */;
      case "monthly":
        return "monthly" /* MONTHLY */;
      case "yearly":
        return "yearly" /* YEARLY */;
      default:
        return "daily" /* DAILY */;
    }
  };
  const createEnhancedEvent = useCallback(async (eventData) => {
    setLoading(true);
    setError(void 0);
    try {
      const validationErrors = EventTypeService.validateEventData(eventData);
      if (validationErrors.length > 0) {
        throw new Error(validationErrors[0]);
      }
      let generatedEvents;
      const currentDate = /* @__PURE__ */ new Date();
      let viewStartDate;
      let viewEndDate;
      switch (eventData.type) {
        case "single" /* SINGLE */:
          viewStartDate = new Date(eventData.startTime);
          viewEndDate = new Date(eventData.endTime);
          generatedEvents = EventTypeService.generateEventInstances(
            eventData,
            viewStartDate,
            viewEndDate
          );
          break;
        case "multi_day" /* MULTI_DAY */:
          viewStartDate = new Date(eventData.startDate);
          viewEndDate = new Date(eventData.endDate);
          generatedEvents = EventTypeService.generateEventInstances(
            eventData,
            viewStartDate,
            viewEndDate
          );
          break;
        case "recurring" /* RECURRING */:
          viewStartDate = new Date(eventData.startDate);
          viewEndDate = eventData.recurrence.endDate || (() => {
            const calcEndDate = new Date(eventData.startDate);
            const count = eventData.recurrence.count || 10;
            const interval = eventData.recurrence.interval || 1;
            switch (eventData.recurrence.pattern) {
              case "daily":
                calcEndDate.setDate(calcEndDate.getDate() + count * interval);
                break;
              case "weekly":
                calcEndDate.setDate(calcEndDate.getDate() + count * interval * 7);
                break;
              case "monthly":
                calcEndDate.setMonth(calcEndDate.getMonth() + count * interval);
                break;
              case "yearly":
                calcEndDate.setFullYear(calcEndDate.getFullYear() + count * interval);
                break;
            }
            return calcEndDate;
          })();
          generatedEvents = EventTypeService.generateEventInstances(
            eventData,
            viewStartDate,
            viewEndDate
          );
          break;
        default:
          throw new Error("\u4E0D\u652F\u6301\u7684\u4E8B\u4EF6\u7C7B\u578B: " + eventData.type);
      }
      const createdEvents = [];
      for (const generatedEvent of generatedEvents) {
        const createRequest = {
          title: generatedEvent.title,
          description: generatedEvent.description,
          startTime: toLocalISOString(generatedEvent.startTime),
          endTime: toLocalISOString(generatedEvent.endTime),
          allDay: generatedEvent.allDay,
          location: generatedEvent.location,
          color: generatedEvent.color,
          priority: mapPriorityToEventPriority(generatedEvent.priority)
        };
        if (eventData.type === "recurring" /* RECURRING */ && eventData.recurrence) {
          createRequest.recurrence = {
            ruleType: mapPatternToRecurrenceType(eventData.recurrence.pattern),
            interval: eventData.recurrence.interval,
            endDate: eventData.recurrence.endDate,
            count: eventData.recurrence.count
          };
        }
        const response = await fetch("/api/calendar/events", {
          method: "POST",
          headers: {
            "Content-Type": "application/json"
          },
          body: JSON.stringify(createRequest)
        });
        if (!response.ok) {
          throw new Error("\u521B\u5EFA\u4E8B\u4EF6\u5B9E\u4F8B\u5931\u8D25: " + response.status);
        }
        const data = await response.json();
        if (!data.success) {
          throw new Error(data.error || "\u521B\u5EFA\u4E8B\u4EF6\u5B9E\u4F8B\u5931\u8D25");
        }
        const newEvent = {
          ...data.data,
          startTime: new Date(data.data.startTime),
          endTime: new Date(data.data.endTime),
          createdAt: new Date(data.data.createdAt),
          updatedAt: new Date(data.data.updatedAt)
        };
        createdEvents.push(newEvent);
      }
      setEvents((prev) => [...prev, ...createdEvents]);
      return createdEvents;
    } catch (err) {
      console.error("\u521B\u5EFA\u589E\u5F3A\u4E8B\u4EF6\u5931\u8D25:", err);
      const errorMessage = err instanceof Error ? err.message : "\u521B\u5EFA\u4E8B\u4EF6\u5931\u8D25";
      setError(errorMessage);
      throw new Error(errorMessage);
    } finally {
      setLoading(false);
    }
  }, []);
  const updateEvent = useCallback(async (eventId, eventData) => {
    setLoading(true);
    setError(void 0);
    try {
      const updateRequest = {};
      if (eventData.title !== void 0) updateRequest.title = eventData.title;
      if (eventData.description !== void 0) updateRequest.description = eventData.description;
      if (eventData.startTime !== void 0) updateRequest.startTime = toLocalISOString(eventData.startTime);
      if (eventData.endTime !== void 0) updateRequest.endTime = toLocalISOString(eventData.endTime);
      if (eventData.allDay !== void 0) updateRequest.allDay = eventData.allDay;
      if (eventData.location !== void 0) updateRequest.location = eventData.location;
      if (eventData.color !== void 0) updateRequest.color = eventData.color;
      const response = await fetch("/api/calendar/events/" + eventId, {
        method: "PUT",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify(updateRequest)
      });
      if (!response.ok) {
        throw new Error("\u66F4\u65B0\u4E8B\u4EF6\u5931\u8D25: " + response.status);
      }
      const data = await response.json();
      if (!data.success) {
        throw new Error(data.error || "\u66F4\u65B0\u4E8B\u4EF6\u5931\u8D25");
      }
      const updatedEvent = {
        ...data.data,
        startTime: new Date(data.data.startTime),
        endTime: new Date(data.data.endTime),
        createdAt: new Date(data.data.createdAt),
        updatedAt: new Date(data.data.updatedAt)
      };
      setEvents((prev) => prev.map(
        (event) => event.id === eventId ? updatedEvent : event
      ));
      return updatedEvent;
    } catch (err) {
      console.error("\u66F4\u65B0\u4E8B\u4EF6\u5931\u8D25:", err);
      const errorMessage = err instanceof Error ? err.message : "\u66F4\u65B0\u4E8B\u4EF6\u5931\u8D25";
      setError(errorMessage);
      throw new Error(errorMessage);
    } finally {
      setLoading(false);
    }
  }, []);
  const deleteEvent = useCallback(async (eventId, deleteAll = false) => {
    setLoading(true);
    setError(void 0);
    try {
      const url = deleteAll ? "/api/calendar/events/" + eventId + "?deleteAll=true" : "/api/calendar/events/" + eventId;
      const response = await fetch(url, {
        method: "DELETE"
      });
      if (!response.ok) {
        throw new Error("\u5220\u9664\u4E8B\u4EF6\u5931\u8D25: " + response.status);
      }
      const data = await response.json();
      if (!data.success) {
        throw new Error(data.error || "\u5220\u9664\u4E8B\u4EF6\u5931\u8D25");
      }
      setEvents((prev) => prev.filter((event) => event.id !== eventId));
    } catch (err) {
      console.error("\u5220\u9664\u4E8B\u4EF6\u5931\u8D25:", err);
      const errorMessage = err instanceof Error ? err.message : "\u5220\u9664\u4E8B\u4EF6\u5931\u8D25";
      setError(errorMessage);
      throw new Error(errorMessage);
    } finally {
      setLoading(false);
    }
  }, []);
  const batchDeleteEvents = useCallback(async (eventIds) => {
    setLoading(true);
    setError(void 0);
    try {
      const response = await fetch("/api/calendar/events/batchDelete", {
        method: "DELETE",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify({ eventIds })
      });
      if (!response.ok) {
        throw new Error("\u6279\u91CF\u5220\u9664\u4E8B\u4EF6\u5931\u8D25: " + response.status);
      }
      const data = await response.json();
      if (!data.success) {
        throw new Error(data.error || "\u6279\u91CF\u5220\u9664\u4E8B\u4EF6\u5931\u8D25");
      }
      setEvents((prev) => prev.filter((event) => !eventIds.includes(event.id)));
    } catch (err) {
      console.error("\u6279\u91CF\u5220\u9664\u4E8B\u4EF6\u5931\u8D25:", err);
      const errorMessage = err instanceof Error ? err.message : "\u6279\u91CF\u5220\u9664\u4E8B\u4EF6\u5931\u8D25";
      setError(errorMessage);
      throw new Error(errorMessage);
    } finally {
      setLoading(false);
    }
  }, []);
  const updateEventTime = useCallback(async (eventId, newStartTime, newEndTime) => {
    setLoading(true);
    setError(void 0);
    console.log("\u{1F504} updateEventTime \u8C03\u7528:", {
      eventId,
      newStartTime: newStartTime.toISOString(),
      newEndTime: newEndTime.toISOString(),
      localStartTime: toLocalISOString(newStartTime),
      localEndTime: toLocalISOString(newEndTime)
    });
    try {
      const updateRequest = {
        startTime: toLocalISOString(newStartTime),
        endTime: toLocalISOString(newEndTime)
      };
      console.log("\u{1F4E4} \u53D1\u9001API\u8BF7\u6C42:", updateRequest);
      const response = await fetch("/api/calendar/events/" + eventId, {
        method: "PUT",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify(updateRequest)
      });
      if (!response.ok) {
        throw new Error("\u66F4\u65B0\u4E8B\u4EF6\u65F6\u95F4\u5931\u8D25: " + response.status);
      }
      const data = await response.json();
      console.log("\u{1F4E5} API\u54CD\u5E94:", data);
      if (!data.success) {
        throw new Error(data.error || "\u66F4\u65B0\u4E8B\u4EF6\u65F6\u95F4\u5931\u8D25");
      }
      console.log("\u{1F504} API\u8FD4\u56DE\u7684\u4E8B\u4EF6\u6570\u636E:", {
        originalStartTime: data.data.startTime,
        originalEndTime: data.data.endTime,
        parsedStartTime: new Date(data.data.startTime).toISOString(),
        parsedEndTime: new Date(data.data.endTime).toISOString()
      });
      const updatedEvent = {
        ...data.data,
        startTime: new Date(data.data.startTime),
        endTime: new Date(data.data.endTime),
        createdAt: new Date(data.data.createdAt),
        updatedAt: new Date(data.data.updatedAt)
      };
      console.log("\u2705 \u66F4\u65B0\u540E\u7684\u4E8B\u4EF6\u5BF9\u8C61:", {
        id: updatedEvent.id,
        title: updatedEvent.title,
        startTime: updatedEvent.startTime.toISOString(),
        endTime: updatedEvent.endTime.toISOString(),
        localStartDate: formatDate(updatedEvent.startTime),
        localEndDate: formatDate(updatedEvent.endTime)
      });
      setEvents((prev) => {
        const oldEvent = prev.find((e) => e.id === eventId);
        const newEvents = prev.map(
          (event) => event.id === eventId ? updatedEvent : event
        );
        console.log("\u{1F504} updateEventTime \u672C\u5730\u72B6\u6001\u66F4\u65B0:", {
          eventId,
          oldEventDate: oldEvent ? formatDate(oldEvent.startTime) : "not found",
          newEventDate: formatDate(updatedEvent.startTime),
          totalEventsBefore: prev.length,
          totalEventsAfter: newEvents.length,
          eventFound: !!oldEvent
        });
        return newEvents;
      });
    } catch (err) {
      console.error("\u66F4\u65B0\u4E8B\u4EF6\u65F6\u95F4\u5931\u8D25:", err);
      const errorMessage = err instanceof Error ? err.message : "\u66F4\u65B0\u4E8B\u4EF6\u65F6\u95F4\u5931\u8D25";
      setError(errorMessage);
      throw new Error(errorMessage);
    } finally {
      setLoading(false);
    }
  }, []);
  return {
    events,
    loading,
    error,
    createEvent,
    createEnhancedEvent,
    updateEvent,
    updateEventTime,
    deleteEvent,
    batchDeleteEvents,
    fetchEvents,
    clearError
  };
}
function useEventDrag(events, onEventUpdate) {
  const [dragState, setDragState] = useState({
    isDragging: false,
    draggedEvent: null,
    dragOverDate: null,
    previewTime: null
  });
  const handleDragStart = useCallback((event) => {
    const eventId = parseInt(event.active.id);
    const draggedEvent = events.find((e) => e.id === eventId);
    console.log("\u{1F3AF} \u62D6\u62FD\u5F00\u59CB:", {
      activeId: event.active.id,
      eventId,
      draggedEvent: draggedEvent ? {
        id: draggedEvent.id,
        title: draggedEvent.title,
        originalStartTime: draggedEvent.startTime,
        originalDate: formatDate(new Date(draggedEvent.startTime))
      } : null
    });
    if (draggedEvent) {
      setDragState({
        isDragging: true,
        draggedEvent,
        dragOverDate: null,
        previewTime: null
      });
    }
  }, [events]);
  const handleDragOver = useCallback((event) => {
    const { over } = event;
    console.log("\u{1F50D} \u62D6\u62FD\u60AC\u505C:", {
      overId: over?.id,
      hasOver: !!over,
      hasDraggedEvent: !!dragState.draggedEvent
    });
    if (over && dragState.draggedEvent) {
      const targetDateStr = over.id;
      console.log("\u{1F4C5} \u89E3\u6790\u76EE\u6807\u65E5\u671F:", {
        targetDateStr,
        startsWithDate: targetDateStr.startsWith("date-")
      });
      if (targetDateStr.startsWith("date-")) {
        const dateStr = targetDateStr.replace("date-", "");
        const parts = dateStr.split("-").map(Number);
        const year = parts[0];
        const month = parts[1];
        const day = parts[2];
        if (year !== void 0 && month !== void 0 && day !== void 0) {
          const targetDate = new Date(year, month - 1, day);
          console.log("\u{1F5D3}\uFE0F \u65E5\u671F\u89E3\u6790\u7ED3\u679C:", {
            dateStr,
            parsedComponents: { year, month: month - 1, day },
            targetDate: targetDate.toISOString(),
            isValidDate: !isNaN(targetDate.getTime()),
            formattedTargetDate: formatDate(targetDate)
          });
          if (!isNaN(targetDate.getTime())) {
            const originalStart = new Date(dragState.draggedEvent.startTime);
            const originalEnd = new Date(dragState.draggedEvent.endTime);
            const newStartTime = new Date(targetDate);
            newStartTime.setHours(originalStart.getHours(), originalStart.getMinutes(), originalStart.getSeconds(), originalStart.getMilliseconds());
            const duration = originalEnd.getTime() - originalStart.getTime();
            const newEndTime = new Date(newStartTime.getTime() + duration);
            const previewTime = dragState.draggedEvent.allDay ? "\u5168\u5929" : formatTime2(newStartTime) + " - " + formatTime2(newEndTime);
            console.log("\u23F0 \u8BA1\u7B97\u65B0\u65F6\u95F4:", {
              originalStart: dragState.draggedEvent.startTime,
              originalEnd: dragState.draggedEvent.endTime,
              newStartTime: newStartTime.toISOString(),
              newEndTime: newEndTime.toISOString(),
              duration: duration / (1e3 * 60),
              // 转换为分钟
              previewTime
            });
            setDragState((prev) => ({
              ...prev,
              dragOverDate: targetDate,
              previewTime
            }));
          }
        }
      }
    }
  }, [dragState.draggedEvent]);
  const handleDragEnd = useCallback(async (event) => {
    const { over } = event;
    console.log("\u{1F3AF} \u62D6\u62FD\u7ED3\u675F:", {
      overId: over?.id,
      hasOver: !!over,
      hasDraggedEvent: !!dragState.draggedEvent,
      hasDragOverDate: !!dragState.dragOverDate,
      dragOverDate: dragState.dragOverDate ? formatDate(dragState.dragOverDate) : null
    });
    if (over && dragState.draggedEvent && dragState.dragOverDate) {
      const originalStart = new Date(dragState.draggedEvent.startTime);
      const originalEnd = new Date(dragState.draggedEvent.endTime);
      const targetDate = dragState.dragOverDate;
      const isSameDayResult = isSameDay(originalStart, targetDate);
      console.log("\u{1F4CA} \u65E5\u671F\u6BD4\u8F83:", {
        originalDate: formatDate(originalStart),
        targetDate: formatDate(targetDate),
        isSameDay: isSameDayResult
      });
      if (!isSameDayResult) {
        try {
          const newStartTime = new Date(targetDate);
          newStartTime.setHours(originalStart.getHours(), originalStart.getMinutes(), originalStart.getSeconds(), originalStart.getMilliseconds());
          const duration = originalEnd.getTime() - originalStart.getTime();
          const newEndTime = new Date(newStartTime.getTime() + duration);
          console.log("\u2705 \u6267\u884C\u62D6\u62FD\u66F4\u65B0\u4E8B\u4EF6:", {
            eventId: dragState.draggedEvent.id,
            originalDate: formatDate(originalStart),
            targetDate: formatDate(targetDate),
            originalStartTime: originalStart.toISOString(),
            originalEndTime: originalEnd.toISOString(),
            newStartTime: newStartTime.toISOString(),
            newEndTime: newEndTime.toISOString(),
            duration: duration / (1e3 * 60)
            // 分钟
          });
          await onEventUpdate(dragState.draggedEvent.id, newStartTime, newEndTime);
        } catch (error) {
          console.error("\u274C \u62D6\u62FD\u66F4\u65B0\u4E8B\u4EF6\u5931\u8D25:", error);
        }
      } else {
        console.log("\u26A0\uFE0F \u8DF3\u8FC7\u66F4\u65B0 - \u65E5\u671F\u76F8\u540C\u6216\u6761\u4EF6\u4E0D\u6EE1\u8DB3");
      }
    } else {
      console.log("\u26A0\uFE0F \u62D6\u62FD\u7ED3\u675F\u4F46\u7F3A\u5C11\u5FC5\u8981\u6761\u4EF6:", {
        hasOver: !!over,
        hasDraggedEvent: !!dragState.draggedEvent,
        hasDragOverDate: !!dragState.dragOverDate
      });
    }
    console.log("\u{1F504} \u91CD\u7F6E\u62D6\u62FD\u72B6\u6001");
    resetDragState();
  }, [dragState.draggedEvent, dragState.dragOverDate, onEventUpdate]);
  const resetDragState = useCallback(() => {
    setDragState({
      isDragging: false,
      draggedEvent: null,
      dragOverDate: null,
      previewTime: null
    });
  }, []);
  return {
    dragState,
    handleDragStart,
    handleDragOver,
    handleDragEnd,
    resetDragState
  };
}
function formatTime2(date) {
  return date.toLocaleTimeString("zh-CN", {
    hour: "2-digit",
    minute: "2-digit",
    hour12: false
  });
}

// src/calendar/services/exportService.ts
var CalendarExportService = class {
  /**
   * 导出事件到指定格式
   */
  static async exportEvents(events, options) {
    switch (options.format) {
      case "ical":
        return this.exportToICal(events);
      case "json":
        return this.exportToJSON(events);
      case "csv":
        return this.exportToCSV(events);
      default:
        throw new Error("\u4E0D\u652F\u6301\u7684\u5BFC\u51FA\u683C\u5F0F: " + options.format);
    }
  }
  /**
   * 导出为 iCal (.ics) 格式
   */
  static exportToICal(events) {
    const lines = [];
    lines.push("BEGIN:VCALENDAR");
    lines.push("VERSION:2.0");
    lines.push("PRODID:-//Profile-v1//Calendar Module//CN");
    lines.push("CALSCALE:GREGORIAN");
    lines.push("METHOD:PUBLISH");
    events.forEach((event) => {
      lines.push("BEGIN:VEVENT");
      lines.push("UID:" + event.id + "@profile-v1.calendar");
      lines.push("DTSTART:" + this.formatDateTimeToICal(new Date(event.startTime)));
      lines.push("DTEND:" + this.formatDateTimeToICal(new Date(event.endTime)));
      lines.push("DTSTAMP:" + this.formatDateTimeToICal(new Date(event.createdAt)));
      lines.push("SUMMARY:" + this.escapeICalText(event.title));
      if (event.description) {
        lines.push("DESCRIPTION:" + this.escapeICalText(event.description));
      }
      if (event.location) {
        lines.push("LOCATION:" + this.escapeICalText(event.location));
      }
      if (event.priority) {
        const priorityMap = {
          "low": "9",
          "normal": "5",
          "high": "1"
        };
        lines.push("PRIORITY:" + (priorityMap[event.priority] || "5"));
      }
      if (event.allDay) {
        lines.push("X-MICROSOFT-CDO-ALLDAYEVENT:TRUE");
      }
      lines.push("END:VEVENT");
    });
    lines.push("END:VCALENDAR");
    return lines.join("\r\n");
  }
  /**
   * 导出为 JSON 格式
   */
  static exportToJSON(events) {
    const exportData = {
      exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
      version: "1.0",
      source: "Profile-v1 Calendar",
      events: events.map((event) => ({
        id: event.id,
        title: event.title,
        description: event.description,
        startTime: event.startTime,
        endTime: event.endTime,
        allDay: event.allDay,
        location: event.location,
        color: event.color,
        priority: event.priority,
        createdAt: event.createdAt,
        updatedAt: event.updatedAt
      }))
    };
    return JSON.stringify(exportData, null, 2);
  }
  /**
   * 导出为 CSV 格式
   */
  static exportToCSV(events) {
    const headers = [
      "ID",
      "\u6807\u9898",
      "\u63CF\u8FF0",
      "\u5F00\u59CB\u65F6\u95F4",
      "\u7ED3\u675F\u65F6\u95F4",
      "\u5168\u5929",
      "\u5730\u70B9",
      "\u989C\u8272",
      "\u4F18\u5148\u7EA7",
      "\u521B\u5EFA\u65F6\u95F4",
      "\u66F4\u65B0\u65F6\u95F4"
    ];
    const rows = events.map((event) => [
      event.id,
      this.escapeCSVField(event.title),
      this.escapeCSVField(event.description || ""),
      event.startTime,
      event.endTime,
      event.allDay ? "\u662F" : "\u5426",
      this.escapeCSVField(event.location || ""),
      event.color || "",
      event.priority || "",
      event.createdAt,
      event.updatedAt || ""
    ]);
    const csvLines = [headers.join(",")];
    rows.forEach((row) => {
      csvLines.push(row.join(","));
    });
    return csvLines.join("\n");
  }
  /**
   * 下载导出的文件
   */
  static downloadFile(content, filename, mimeType) {
    const blob = new Blob([content], { type: mimeType });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = filename;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
  }
  /**
   * 获导出方法的便捷函数
   */
  static async exportAndDownload(events, options, filename) {
    const content = await this.exportEvents(events, options);
    const defaultFilenames = {
      ical: "calendar-events.ics",
      json: "calendar-events.json",
      csv: "calendar-events.csv"
    };
    const mimeTypes = {
      ical: "text/calendar",
      json: "application/json",
      csv: "text/csv"
    };
    const finalFilename = filename || defaultFilenames[options.format];
    const mimeType = mimeTypes[options.format];
    this.downloadFile(content, finalFilename, mimeType);
  }
  /**
   * 格式化日期时间为 iCal 格式
   */
  static formatDateTimeToICal(date) {
    return date.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}/, "");
  }
  /**
   * 转义 iCal 文本
   */
  static escapeICalText(text) {
    return text.replace(/\\/g, "\\\\").replace(/;/g, "\\;").replace(/,/g, "\\,").replace(/\n/g, "\\n").replace(/\r/g, "");
  }
  /**
   * 转义 CSV 字段
   */
  static escapeCSVField(field) {
    if (field.includes(",") || field.includes('"') || field.includes("\n")) {
      return '"' + field.replace(/"/g, '""') + '"';
    }
    return field;
  }
};
var exportService_default = CalendarExportService;

// src/calendar/services/importService.ts
var CalendarImportService = class {
  /**
   * 从文件导入事件
   */
  static async importFromFile(file, options) {
    try {
      const content = await this.readFileContent(file);
      return await this.importFromContent(content, options);
    } catch (error) {
      return {
        success: false,
        importedCount: 0,
        errors: ["\u8BFB\u53D6\u6587\u4EF6\u5931\u8D25: " + (error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF")],
        events: []
      };
    }
  }
  /**
   * 从内容字符串导入事件
   */
  static async importFromContent(content, options) {
    try {
      let events = [];
      switch (options.format) {
        case "ical":
          events = this.parseICalContent(content);
          break;
        case "json":
          events = this.parseJSONContent(content);
          break;
        case "csv":
          events = this.parseCSVContent(content);
          break;
        default:
          throw new Error("\u4E0D\u652F\u6301\u7684\u5BFC\u5165\u683C\u5F0F: " + options.format);
      }
      if (options.validateEvents) {
        events = this.validateAndFilterEvents(events);
      }
      return {
        success: true,
        importedCount: events.length,
        errors: [],
        events
      };
    } catch (error) {
      return {
        success: false,
        importedCount: 0,
        errors: ["\u89E3\u6790\u5185\u5BB9\u5931\u8D25: " + (error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF")],
        events: []
      };
    }
  }
  /**
   * 读取文件内容
   */
  static readFileContent(file) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = (e) => resolve(e.target?.result);
      reader.onerror = () => reject(new Error("\u6587\u4EF6\u8BFB\u53D6\u5931\u8D25"));
      reader.readAsText(file);
    });
  }
  /**
   * 解析 iCal 内容
   */
  static parseICalContent(content) {
    const events = [];
    const lines = content.split(/\r?\n/);
    let currentEvent = null;
    let currentProperty = "";
    for (let line of lines) {
      line = line.trim();
      if (line === "BEGIN:VEVENT") {
        currentEvent = {};
        continue;
      }
      if (line === "END:VEVENT") {
        if (currentEvent && currentEvent.title && currentEvent.startTime && currentEvent.endTime) {
          events.push(currentEvent);
        }
        currentEvent = null;
        continue;
      }
      if (!currentEvent) continue;
      if (line.startsWith(" ") && currentProperty) {
        continue;
      }
      const colonIndex = line.indexOf(":");
      if (colonIndex === -1) continue;
      const property = line.substring(0, colonIndex);
      const value = line.substring(colonIndex + 1);
      currentProperty = property;
      switch (property) {
        case "SUMMARY":
          currentEvent.title = this.unescapeICalText(value);
          break;
        case "DESCRIPTION":
          currentEvent.description = this.unescapeICalText(value);
          break;
        case "DTSTART":
          currentEvent.startTime = this.parseICalDateTime(value);
          break;
        case "DTEND":
          currentEvent.endTime = this.parseICalDateTime(value);
          break;
        case "LOCATION":
          currentEvent.location = this.unescapeICalText(value);
          break;
        case "PRIORITY":
          const priority = this.mapICalPriority(value);
          if (priority) currentEvent.priority = priority;
          break;
      }
    }
    return events;
  }
  /**
   * 解析 JSON 内容
   */
  static parseJSONContent(content) {
    const data = JSON.parse(content);
    if (data.events && Array.isArray(data.events)) {
      return data.events.map((event) => ({
        title: event.title,
        description: event.description,
        startTime: event.startTime,
        endTime: event.endTime,
        allDay: event.allDay || false,
        location: event.location,
        color: event.color,
        priority: event.priority
      }));
    }
    if (Array.isArray(data)) {
      return data.map((event) => ({
        title: event.title,
        description: event.description,
        startTime: event.startTime,
        endTime: event.endTime,
        allDay: event.allDay || false,
        location: event.location,
        color: event.color,
        priority: event.priority
      }));
    }
    throw new Error("\u65E0\u6548\u7684 JSON \u683C\u5F0F");
  }
  /**
   * 解析 CSV 内容
   */
  static parseCSVContent(content) {
    const lines = content.split(/\r?\n/);
    if (lines.length < 2) {
      throw new Error("CSV \u6587\u4EF6\u5FC5\u987B\u5305\u542B\u6807\u9898\u884C\u548C\u81F3\u5C11\u4E00\u884C\u6570\u636E");
    }
    const firstLine = lines[0];
    if (firstLine === void 0) {
      throw new Error("CSV \u6587\u4EF6\u4E3A\u7A7A");
    }
    const headers = this.parseCSVLine(firstLine);
    const events = [];
    for (let i = 1; i < lines.length; i++) {
      const currentLine = lines[i];
      if (currentLine === void 0) continue;
      const line = currentLine.trim();
      if (!line) continue;
      const values = this.parseCSVLine(line);
      if (values.length !== headers.length) continue;
      const event = {};
      headers.forEach((header, index) => {
        const value = values[index];
        switch (header.toLowerCase()) {
          case "\u6807\u9898":
          case "title":
            event.title = value;
            break;
          case "\u63CF\u8FF0":
          case "description":
            event.description = value;
            break;
          case "\u5F00\u59CB\u65F6\u95F4":
          case "start_time":
          case "starttime":
            event.startTime = value;
            break;
          case "\u7ED3\u675F\u65F6\u95F4":
          case "end_time":
          case "endtime":
            event.endTime = value;
            break;
          case "\u5168\u5929":
          case "all_day":
          case "allday":
            event.allDay = value === "\u662F" || value === "true" || value === "True";
            break;
          case "\u5730\u70B9":
          case "location":
            event.location = value;
            break;
          case "\u989C\u8272":
          case "color":
            event.color = value;
            break;
          case "\u4F18\u5148\u7EA7":
          case "priority":
            event.priority = value;
            break;
        }
      });
      if (event.title && event.startTime && event.endTime) {
        events.push(event);
      }
    }
    return events;
  }
  /**
   * 解析 CSV 行
   */
  static parseCSVLine(line) {
    const result = [];
    let current = "";
    let inQuotes = false;
    for (let i = 0; i < line.length; i++) {
      const char = line[i];
      if (char === '"') {
        if (inQuotes && line[i + 1] === '"') {
          current += '"';
          i++;
        } else {
          inQuotes = !inQuotes;
        }
      } else if (char === "," && !inQuotes) {
        result.push(current);
        current = "";
      } else {
        current += char;
      }
    }
    result.push(current);
    return result;
  }
  /**
   * 验证和过滤事件
   */
  static validateAndFilterEvents(events) {
    return events.filter((event) => {
      if (!event.title || !event.startTime || !event.endTime) {
        return false;
      }
      const startTime = new Date(event.startTime);
      const endTime = new Date(event.endTime);
      if (isNaN(startTime.getTime()) || isNaN(endTime.getTime())) {
        return false;
      }
      if (endTime <= startTime) {
        return false;
      }
      return true;
    });
  }
  /**
   * 解析 iCal 日期时间
   */
  static parseICalDateTime(value) {
    if (value.includes("T")) {
      const dateTime = value.replace(/[^\d]/g, "");
      if (dateTime.length >= 14) {
        const year = dateTime.substring(0, 4);
        const month = dateTime.substring(4, 6);
        const day = dateTime.substring(6, 8);
        const hour = dateTime.substring(8, 10);
        const minute = dateTime.substring(10, 12);
        const second = dateTime.substring(12, 14);
        return year + "-" + month + "-" + day + "T" + hour + ":" + minute + ":" + second + ".000Z";
      }
    } else {
      const dateOnly = value.replace(/[^\d]/g, "");
      if (dateOnly.length >= 8) {
        const year = dateOnly.substring(0, 4);
        const month = dateOnly.substring(4, 6);
        const day = dateOnly.substring(6, 8);
        return year + "-" + month + "-" + day + "T00:00:00.000Z";
      }
    }
    return (/* @__PURE__ */ new Date()).toISOString();
  }
  /**
   * 反转义 iCal 文本
   */
  static unescapeICalText(text) {
    return text.replace(/\\n/g, "\n").replace(/\\,/g, ",").replace(/\\;/g, ";").replace(/\\\\/g, "\\");
  }
  /**
   * 映射 iCal 优先级
   */
  static mapICalPriority(value) {
    const priority = parseInt(value);
    if (priority >= 1 && priority <= 3) return "high";
    if (priority >= 4 && priority <= 6) return "normal";
    if (priority >= 7 && priority <= 9) return "low";
    return void 0;
  }
};
var importService_default = CalendarImportService;

// src/calendar/services/recurrenceService.ts
var RecurrenceService = class {
  /**
   * 根据重复规则生成事件实例
   */
  static generateRecurringInstances(baseEvent, rule, startDate, endDate, eventId) {
    const instances = [];
    const baseStart = new Date(baseEvent.startTime);
    const baseEnd = new Date(baseEvent.endTime);
    const duration = baseEnd.getTime() - baseStart.getTime();
    let currentDate = new Date(startDate);
    let instanceCount = 0;
    const maxInstances = rule.count || 1e3;
    const ruleEndDate = rule.endDate ? new Date(rule.endDate) : endDate;
    const finalEndDate = ruleEndDate < endDate ? ruleEndDate : endDate;
    while (currentDate <= finalEndDate && instanceCount < maxInstances) {
      if (this.shouldGenerateInstance(currentDate, baseStart, rule)) {
        const instanceStart = new Date(currentDate);
        instanceStart.setHours(baseStart.getHours(), baseStart.getMinutes(), baseStart.getSeconds());
        const instanceEnd = new Date(instanceStart.getTime() + duration);
        instances.push({
          startTime: instanceStart.toISOString(),
          endTime: instanceEnd.toISOString(),
          title: baseEvent.title,
          description: baseEvent.description,
          location: baseEvent.location,
          color: baseEvent.color,
          priority: baseEvent.priority,
          allDay: baseEvent.allDay,
          isRecurringInstance: true,
          recurrenceId: eventId,
          instanceDate: currentDate.toISOString().split("T")[0]
        });
        instanceCount++;
      }
      currentDate = this.getNextOccurrence(currentDate, rule);
      if (instanceCount > maxInstances) {
        break;
      }
    }
    return instances;
  }
  /**
   * 判断是否应该在指定日期生成实例
   */
  static shouldGenerateInstance(date, baseDate, rule) {
    switch (rule.type) {
      case "daily":
        const daysDiff = Math.floor((date.getTime() - baseDate.getTime()) / (24 * 60 * 60 * 1e3));
        return daysDiff >= 0 && daysDiff % rule.interval === 0;
      case "weekly":
        const weeksDiff = Math.floor((date.getTime() - baseDate.getTime()) / (7 * 24 * 60 * 60 * 1e3));
        const sameWeekday = date.getDay() === baseDate.getDay();
        return weeksDiff >= 0 && weeksDiff % rule.interval === 0 && sameWeekday;
      case "monthly":
        const monthsDiff = (date.getFullYear() - baseDate.getFullYear()) * 12 + (date.getMonth() - baseDate.getMonth());
        const sameDayOfMonth = date.getDate() === baseDate.getDate();
        return monthsDiff >= 0 && monthsDiff % rule.interval === 0 && sameDayOfMonth;
      case "yearly":
        const yearsDiff = date.getFullYear() - baseDate.getFullYear();
        const sameMonthAndDay = date.getMonth() === baseDate.getMonth() && date.getDate() === baseDate.getDate();
        return yearsDiff >= 0 && yearsDiff % rule.interval === 0 && sameMonthAndDay;
      default:
        return false;
    }
  }
  /**
   * 获取下一个重复事件的日期
   */
  static getNextOccurrence(date, rule) {
    const nextDate = new Date(date);
    switch (rule.type) {
      case "daily":
        nextDate.setDate(nextDate.getDate() + rule.interval);
        break;
      case "weekly":
        nextDate.setDate(nextDate.getDate() + 7 * rule.interval);
        break;
      case "monthly":
        nextDate.setMonth(nextDate.getMonth() + rule.interval);
        break;
      case "yearly":
        nextDate.setFullYear(nextDate.getFullYear() + rule.interval);
        break;
    }
    return nextDate;
  }
  /**
   * 验证重复规则
   */
  static validateRecurrenceRule(rule) {
    const errors = [];
    if (!rule.type) {
      errors.push("\u91CD\u590D\u7C7B\u578B\u4E0D\u80FD\u4E3A\u7A7A");
    }
    if (!rule.interval || rule.interval < 1) {
      errors.push("\u91CD\u590D\u95F4\u9694\u5FC5\u987B\u5927\u4E8E0");
    }
    if (rule.interval > 365) {
      errors.push("\u91CD\u590D\u95F4\u9694\u4E0D\u80FD\u8D85\u8FC7365");
    }
    if (rule.endDate && rule.count && rule.count > 0) {
      errors.push("\u4E0D\u80FD\u540C\u65F6\u8BBE\u7F6E\u7ED3\u675F\u65E5\u671F\u548C\u91CD\u590D\u6B21\u6570");
    }
    if (rule.endDate) {
      const endDate = new Date(rule.endDate);
      if (isNaN(endDate.getTime())) {
        errors.push("\u7ED3\u675F\u65E5\u671F\u683C\u5F0F\u4E0D\u6B63\u786E");
      } else if (endDate < /* @__PURE__ */ new Date()) {
        errors.push("\u7ED3\u675F\u65E5\u671F\u4E0D\u80FD\u65E9\u4E8E\u5F53\u524D\u65E5\u671F");
      }
    }
    if (rule.count && (rule.count < 1 || rule.count > 999)) {
      errors.push("\u91CD\u590D\u6B21\u6570\u5FC5\u987B\u57281-999\u4E4B\u95F4");
    }
    return errors;
  }
  /**
   * 将表单数据转换为重复规则
   */
  static formDataToRecurrenceRule(formData) {
    if (!formData.recurrenceType) {
      return null;
    }
    return {
      type: formData.recurrenceType,
      interval: formData.recurrenceInterval || 1,
      endDate: formData.recurrenceEndDate || void 0,
      count: formData.recurrenceCount || void 0
    };
  }
  /**
   * 描述重复规则的文本
   */
  static describeRecurrenceRule(rule) {
    let description = "";
    switch (rule.type) {
      case "daily":
        description = rule.interval === 1 ? "\u6BCF\u5929" : "\u6BCF" + rule.interval + "\u5929";
        break;
      case "weekly":
        description = rule.interval === 1 ? "\u6BCF\u5468" : "\u6BCF" + rule.interval + "\u5468";
        break;
      case "monthly":
        description = rule.interval === 1 ? "\u6BCF\u6708" : "\u6BCF" + rule.interval + "\u4E2A\u6708";
        break;
      case "yearly":
        description = rule.interval === 1 ? "\u6BCF\u5E74" : "\u6BCF" + rule.interval + "\u5E74";
        break;
    }
    if (rule.endDate) {
      description += "\uFF0C\u76F4\u5230" + rule.endDate;
    } else if (rule.count) {
      description += "\uFF0C\u5171" + rule.count + "\u6B21";
    }
    return description;
  }
};

// src/calendar/services/reminderService.ts
var ReminderService = class {
  /**
   * 初始化提醒服务
   */
  static async initialize() {
    if ("Notification" in window) {
      this.notificationPermission = await Notification.requestPermission();
    }
    this.checkPendingReminders();
    setInterval(() => {
      this.checkPendingReminders();
    }, 6e4);
  }
  /**
   * 为事件创建提醒
   */
  static createReminder(event, reminderMinutes, reminderType = "browser") {
    const reminderId = event.id + "_" + Date.now();
    const eventStart = new Date(event.startTime);
    const reminderTime = new Date(eventStart.getTime() - reminderMinutes * 60 * 1e3);
    const reminder = {
      id: reminderId,
      eventId: event.id,
      eventTitle: event.title,
      reminderTime,
      config: {
        minutes: reminderMinutes,
        type: reminderType,
        enabled: true
      },
      status: "pending"
    };
    this.reminders.set(reminderId, reminder);
    if (reminderTime <= /* @__PURE__ */ new Date()) {
      this.triggerReminder(reminder);
    }
    return reminderId;
  }
  /**
   * 删除提醒
   */
  static deleteReminder(reminderId) {
    return this.reminders.delete(reminderId);
  }
  /**
   * 删除事件的所有提醒
   */
  static deleteEventReminders(eventId) {
    for (const [id, reminder] of this.reminders.entries()) {
      if (reminder.eventId === eventId) {
        this.reminders.delete(id);
      }
    }
  }
  /**
   * 检查并触发到期的提醒
   */
  static checkPendingReminders() {
    const now = /* @__PURE__ */ new Date();
    for (const reminder of this.reminders.values()) {
      if (reminder.status === "pending" && reminder.reminderTime <= now) {
        this.triggerReminder(reminder);
      }
    }
  }
  /**
   * 触发提醒
   */
  static async triggerReminder(reminder) {
    try {
      switch (reminder.config.type) {
        case "browser":
          await this.sendBrowserNotification(reminder);
          break;
        case "email":
          await this.sendEmailReminder(reminder);
          break;
        case "sms":
          await this.sendSmsReminder(reminder);
          break;
        case "sound":
          await this.playSoundReminder(reminder);
          break;
      }
      reminder.status = "sent";
    } catch (error) {
      console.error("Failed to send reminder:", error);
      reminder.status = "failed";
    }
  }
  /**
   * 发送浏览器通知
   */
  static async sendBrowserNotification(reminder) {
    if (this.notificationPermission !== "granted") {
      throw new Error("Notification permission not granted");
    }
    const timeText = this.getTimeText(reminder.config.minutes);
    const notification = new Notification(`\u{1F4C5} \u4E8B\u4EF6\u63D0\u9192`, {
      body: reminder.eventTitle + "\n" + timeText,
      icon: "/favicon.ico",
      tag: reminder.id,
      requireInteraction: true
    });
    notification.onclick = () => {
      window.focus();
      notification.close();
    };
    setTimeout(() => {
      notification.close();
    }, 1e4);
  }
  /**
   * 发送邮件提醒
   */
  static async sendEmailReminder(reminder) {
    console.log("Email reminder would be sent:", reminder);
  }
  /**
   * 发送短信提醒
   */
  static async sendSmsReminder(reminder) {
    console.log("SMS reminder would be sent:", reminder);
  }
  /**
   * 播放声音提醒
   */
  static async playSoundReminder(reminder) {
    try {
      const audioContext = new (window.AudioContext || window.webkitAudioContext)();
      const oscillator = audioContext.createOscillator();
      const gainNode = audioContext.createGain();
      oscillator.connect(gainNode);
      gainNode.connect(audioContext.destination);
      oscillator.frequency.setValueAtTime(800, audioContext.currentTime);
      oscillator.frequency.setValueAtTime(600, audioContext.currentTime + 0.1);
      oscillator.frequency.setValueAtTime(800, audioContext.currentTime + 0.2);
      gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
      gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5);
      oscillator.start(audioContext.currentTime);
      oscillator.stop(audioContext.currentTime + 0.5);
      this.showVisualAlert(reminder);
    } catch (error) {
      console.error("Failed to play sound reminder:", error);
      this.showVisualAlert(reminder);
    }
  }
  /**
   * 显示视觉提醒
   */
  static showVisualAlert(reminder) {
    const timeText = this.getTimeText(reminder.config.minutes);
    alert("\u{1F4C5} \u4E8B\u4EF6\u63D0\u9192\n\n" + reminder.eventTitle + "\n" + timeText);
  }
  /**
   * 获取时间描述文本
   */
  static getTimeText(minutes) {
    if (minutes === 0) {
      return "\u4E8B\u4EF6\u5373\u5C06\u5F00\u59CB";
    } else if (minutes < 60) {
      return minutes + "\u5206\u949F\u540E\u5F00\u59CB";
    } else if (minutes < 1440) {
      const hours = Math.floor(minutes / 60);
      const remainingMinutes = minutes % 60;
      return remainingMinutes > 0 ? hours + "\u5C0F\u65F6" + remainingMinutes + "\u5206\u949F\u540E\u5F00\u59CB" : hours + "\u5C0F\u65F6\u540E\u5F00\u59CB";
    } else {
      const days = Math.floor(minutes / 1440);
      const remainingHours = Math.floor(minutes % 1440 / 60);
      return remainingHours > 0 ? days + "\u5929" + remainingHours + "\u5C0F\u65F6\u540E\u5F00\u59CB" : days + "\u5929\u540E\u5F00\u59CB";
    }
  }
  /**
   * 获取所有提醒
   */
  static getAllReminders() {
    return Array.from(this.reminders.values());
  }
  /**
   * 获取事件的提醒
   */
  static getEventReminders(eventId) {
    return Array.from(this.reminders.values()).filter(
      (reminder) => reminder.eventId === eventId
    );
  }
  /**
   * 智能推荐提醒时间
   */
  static getSmartReminderSuggestions(event) {
    const suggestions = [];
    const eventStart = new Date(event.startTime);
    const now = /* @__PURE__ */ new Date();
    const hoursUntilEvent = (eventStart.getTime() - now.getTime()) / (1e3 * 60 * 60);
    if (event.allDay) {
      suggestions.push(1440, 720, 60);
    } else {
      if (hoursUntilEvent > 24) {
        suggestions.push(1440, 60, 15);
      } else if (hoursUntilEvent > 2) {
        suggestions.push(60, 30, 15);
      } else {
        suggestions.push(30, 15, 5);
      }
    }
    if (event.priority === "urgent") {
      suggestions.unshift(0);
    }
    return [...new Set(suggestions)].sort((a, b) => b - a);
  }
  /**
   * 批量创建智能提醒
   */
  static createSmartReminders(event) {
    const suggestions = this.getSmartReminderSuggestions(event);
    const reminderIds = [];
    suggestions.slice(0, 2).forEach((minutes) => {
      const reminderId = this.createReminder(event, minutes, "browser");
      reminderIds.push(reminderId);
    });
    return reminderIds;
  }
};
ReminderService.reminders = /* @__PURE__ */ new Map();
ReminderService.notificationPermission = "default";
var isMobileDevice = () => {
  if (typeof window === "undefined") return false;
  const isMobileWidth = window.innerWidth <= 768;
  const isTouchDevice = "ontouchstart" in window || navigator.maxTouchPoints > 0;
  const isMobileUserAgent = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
    navigator.userAgent
  );
  return isMobileWidth || isTouchDevice && isMobileUserAgent;
};
var isDragSupported = () => {
  if (typeof window === "undefined") return false;
  if (isMobileDevice()) return false;
  return "draggable" in document.createElement("div");
};
var useDeviceType = () => {
  const [isMobile, setIsMobile] = useState(false);
  const [dragSupported, setDragSupported] = useState(true);
  useEffect(() => {
    const checkDevice = () => {
      const mobile = isMobileDevice();
      setIsMobile(mobile);
      setDragSupported(isDragSupported());
    };
    checkDevice();
    window.addEventListener("resize", checkDevice);
    return () => window.removeEventListener("resize", checkDevice);
  }, []);
  return { isMobile, dragSupported };
};

// src/calendar/core/index.ts
var CALENDAR_MODULE_VERSION = "1.0.0";
var CALENDAR_MODULE_NAME = "sa2kit/calendar";

export { CALENDAR_MODULE_NAME, CALENDAR_MODULE_VERSION, exportService_default as CalendarExportService, importService_default as CalendarImportService, CalendarViewType, EventColor, EventListDisplayMode, EventPriority, EventSortField, EventType, EventTypeService, RecurrencePattern, RecurrenceService, RecurrenceType, ReminderService, ReminderStatus, ReminderType, SortDirection, addDays, addMonths, addWeeks, addYears, cloneDate, formatDate, formatDateTime, formatTime, generateTimeSlots, getDayEnd, getDayStart, getDaysDifference, getMonthEnd, getMonthName, getMonthStart, getMonthViewDates, getRelativeTime, getWeekEnd, getWeekStart, getWeekViewDates, getWeekdayName, isDragSupported, isMobileDevice, isSameDay, isSameMonth, isSameWeek, isToday, isValidDate, isWeekend, isWorkingHour, parseDate, toLocalISOString, useDeviceType, useEnhancedEvents, useEventDrag, useEvents };
//# sourceMappingURL=index.mjs.map
//# sourceMappingURL=index.mjs.map