import { GoogleCalendarEvent, getUserCalendars as getGoogleCalendars, getUpcomingEvents as getGoogleEvents, initializeOAuth as initializeGoogleOAuth } from './google-calendar';
import { AppleCalendarEvent, getUserCalendars as getAppleCalendars, getUpcomingEvents as getAppleEvents, getUpcomingEventsBatch as getAppleEventsBatch } from './apple-calendar';
import { getUpcomingEvents as getOutlookEvents, refreshAccessToken as refreshOutlookTokenAPI, createEvent as createOutlookEvent, updateEvent as updateOutlookEvent, deleteEvent as deleteOutlookEvent } from './outlook-calendar';
import { getUserCalendars as getSynologyCalendars, getUpcomingEvents as getSynologyEvents, getUpcomingEventsBatch as getSynologyEventsBatch } from './synology-calendar';
import { getUpcomingEvents as getNotionEvents, refreshAccessToken as refreshNotionToken } from './notion-calendar';
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

export interface EventReminder {
  method: 'popup' | 'email' | 'display'; // display = VALARM DISPLAY, popup = Google popup, email = email reminder
  minutes: number; // minutes before event
}

export interface EventAttendee {
  email: string;
  displayName?: string;
  responseStatus?: 'needsAction' | 'accepted' | 'declined' | 'tentative';
}

export interface EventAttachment {
  url: string;
  title?: string;
}

export type BusyStatus = 'free' | 'tentative' | 'busy' | 'oof' | 'workingElsewhere';
export type EventVisibility = 'default' | 'public' | 'private' | 'confidential';

export interface CalendarEvent {
  id: string;
  title: string;
  description?: string;
  start: {
    dateTime?: string;
    date?: string;
  };
  end: {
    dateTime?: string;
    date?: string;
  };
  location?: string;
  url?: string;
  recurrence?: string;
  recurrenceEndDate?: string;
  recurrenceCount?: number;
  recurrenceInterval?: number;
  recurrenceDays?: number[];
  timezone?: string;
  recurringEventId?: string;
  isRecurring?: boolean;
  source: 'google' | 'apple' | 'outlook' | 'synology' | 'notion';
  calendarId: string;
  calendarTitle: string;
  backgroundColor?: string;
  allDay?: boolean;
  reminders?: EventReminder[];
  busyStatus?: BusyStatus;
  visibility?: EventVisibility;
  attendees?: EventAttendee[];
  attachments?: EventAttachment[];
}

/**
 * Convert friendly recurrence name to RRULE string
 */
function toRRule(recurrence?: string, recurrenceEndDate?: string, recurrenceCount?: number, recurrenceInterval?: number, recurrenceDays?: number[]): string | null {
  let freq: string;
  switch (recurrence) {
    case 'daily': freq = 'FREQ=DAILY'; break;
    case 'weekly': freq = 'FREQ=WEEKLY'; break;
    case 'biweekly': freq = 'FREQ=WEEKLY;INTERVAL=2'; break;
    case 'monthly': freq = 'FREQ=MONTHLY'; break;
    case 'yearly': freq = 'FREQ=YEARLY'; break;
    default: return null;
  }
  let rrule = `RRULE:${freq}`;
  if (recurrenceInterval && recurrenceInterval > 1 && recurrence !== 'biweekly') {
    rrule += `;INTERVAL=${recurrenceInterval}`;
  }
  if (recurrenceDays && recurrenceDays.length > 0 && recurrence === 'weekly') {
    const dayMap = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'];
    rrule += `;BYDAY=${recurrenceDays.map(d => dayMap[d]).join(',')}`;
  }
  if (recurrenceCount && recurrenceCount > 0) {
    rrule += `;COUNT=${recurrenceCount}`;
  } else if (recurrenceEndDate) {
    const d = new Date(recurrenceEndDate);
    d.setHours(23, 59, 59);
    rrule += `;UNTIL=${d.toISOString().replace(/[-:]/g, '').replace(/\.\d+Z$/, 'Z')}`;
  }
  return rrule;
}

/**
 * Convert friendly recurrence name to Outlook Graph recurrence object
 */
function toOutlookRecurrence(recurrence?: string, startDate?: Date, recurrenceEndDate?: string, recurrenceCount?: number, recurrenceInterval?: number, recurrenceDays?: number[]): any {
  if (!recurrence || recurrence === 'none') return undefined;
  const start = startDate || new Date();
  const interval = recurrenceInterval || (recurrence === 'biweekly' ? 2 : 1);
  const dayNames = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
  let range: any = {
    type: 'noEnd',
    startDate: start.toISOString().split('T')[0],
  };
  if (recurrenceCount && recurrenceCount > 0) {
    range = { type: 'numbered', startDate: start.toISOString().split('T')[0], numberOfOccurrences: recurrenceCount };
  } else if (recurrenceEndDate) {
    range = { type: 'endDate', startDate: start.toISOString().split('T')[0], endDate: recurrenceEndDate };
  }
  switch (recurrence) {
    case 'daily':
      return { pattern: { type: 'daily', interval }, range };
    case 'weekly':
    case 'biweekly': {
      const days = recurrenceDays && recurrenceDays.length > 0
        ? recurrenceDays.map(d => dayNames[d])
        : [dayNames[start.getDay()]];
      return { pattern: { type: 'weekly', interval, daysOfWeek: days, firstDayOfWeek: 'monday' }, range };
    }
    case 'monthly':
      return { pattern: { type: 'absoluteMonthly', interval, dayOfMonth: start.getDate() }, range };
    case 'yearly':
      return { pattern: { type: 'absoluteYearly', interval, dayOfMonth: start.getDate(), month: start.getMonth() + 1 }, range };
    default: return undefined;
  }
}

// Google Calendar event color mapping (colorId -> hex color)
const GOOGLE_EVENT_COLORS: Record<string, string> = {
  '1': '#7986cb', // Lavender
  '2': '#33b679', // Sage
  '3': '#8e24aa', // Grape
  '4': '#e67c73', // Flamingo
  '5': '#f6bf26', // Banana
  '6': '#f4511e', // Tangerine
  '7': '#039be5', // Peacock
  '8': '#616161', // Graphite
  '9': '#3f51b5', // Blueberry
  '10': '#0b8043', // Basil
  '11': '#d50000', // Tomato
};

function getGoogleEventColor(colorId: string): string {
  return GOOGLE_EVENT_COLORS[colorId] || '#039be5'; // Default to Peacock
}

export interface CalendarConnection {
  id: string;
  provider: 'google' | 'apple' | 'outlook' | 'synology';
  accessToken: string;
  refreshToken?: string;
  expiresAt?: Date;
  calendars?: Array<{
    id: string;
    title: string;
    isPrimary?: boolean;
    selected?: boolean;
    backgroundColor?: string;
  }> | any; // Type it loosely for JSON compatibility
}

/**
 * Refresh the Google access token using the refresh token
 */
async function refreshGoogleToken(connection: CalendarConnection): Promise<string | null> {
  if (!connection.refreshToken) {
    console.log('[CALENDAR] No refresh token available for connection:', connection.id);
    return null;
  }

  try {
    const oauth2Client = initializeGoogleOAuth(
      process.env.GOOGLE_CLIENT_ID || '',
      process.env.GOOGLE_CLIENT_SECRET || '',
      process.env.GOOGLE_REDIRECT_URI || ''
    );

    oauth2Client.setCredentials({
      refresh_token: connection.refreshToken
    });

    console.log('[CALENDAR] Refreshing Google access token...');
    const { credentials } = await oauth2Client.refreshAccessToken();

    if (credentials.access_token) {
      // Update the token in the database
      await prisma.calendarConnection.update({
        where: { id: connection.id },
        data: {
          accessToken: credentials.access_token,
          expiresAt: credentials.expiry_date ? new Date(credentials.expiry_date) : null
        }
      });
      console.log('[CALENDAR] Token refreshed successfully');
      return credentials.access_token;
    }
  } catch (error) {
    console.error('[CALENDAR] Failed to refresh token:', error);
  }
  return null;
}

/**
 * Refresh the Outlook access token using the refresh token
 */
async function refreshOutlookToken(connection: CalendarConnection): Promise<string | null> {
  if (!connection.refreshToken) {
    console.log('[CALENDAR] No refresh token available for connection:', connection.id);
    return null;
  }

  try {
    console.log('[CALENDAR] Refreshing Outlook access token...');
    const data = await refreshOutlookTokenAPI(connection.refreshToken);

    if (data.access_token) {
      // Update the token in the database
      const expiresAt = new Date();
      expiresAt.setSeconds(expiresAt.getSeconds() + data.expires_in);

      await prisma.calendarConnection.update({
        where: { id: connection.id },
        data: {
          accessToken: data.access_token,
          refreshToken: data.refresh_token || connection.refreshToken, // Update refresh token if provided
          expiresAt
        }
      });
      console.log('[CALENDAR] Outlook token refreshed successfully');
      return data.access_token;
    }
  } catch (error) {
    console.error('[CALENDAR] Failed to refresh Outlook token:', error);
  }
  return null;
}

/**
 * Check if the token is expired or about to expire
 */
function isTokenExpired(expiresAt?: Date): boolean {
  if (!expiresAt) return true; // Assume expired if no expiry info

  // Consider token expired if it expires within the next 5 minutes
  const bufferMs = 5 * 60 * 1000;
  return new Date().getTime() > (new Date(expiresAt).getTime() - bufferMs);
}

/**
 * Fetch calendar events from connected accounts
 */
export const getCalendarEvents = async (
  connections: CalendarConnection[],
  timeMin: string,
  timeMax: string
): Promise<CalendarEvent[]> => {
  const allEvents: CalendarEvent[] = [];

  console.log('[CALENDAR] Fetching events from', connections.length, 'connections');
  console.log('[CALENDAR] Date range:', timeMin, 'to', timeMax);

  for (const connection of connections) {
    try {
      let events: any[] = [];
      let accessToken = connection.accessToken;

      if (connection.provider === 'google') {
        console.log('[CALENDAR] Processing Google connection:', connection.id);

        // Check if token is expired and refresh if needed
        if (isTokenExpired(connection.expiresAt)) {
          console.log('[CALENDAR] Token expired, attempting refresh...');
          const newToken = await refreshGoogleToken(connection);
          if (newToken) {
            accessToken = newToken;
          } else {
            console.error('[CALENDAR] Failed to refresh token, skipping connection');
            continue;
          }
        }

        // Initialize OAuth client
        const oauth2Client = initializeGoogleOAuth(
          process.env.GOOGLE_CLIENT_ID || '',
          process.env.GOOGLE_CLIENT_SECRET || '',
          process.env.GOOGLE_REDIRECT_URI || ''
        );

        // Identify which calendars to fetch events from
        let calendarIds: string[] = [];
        let calendars: any[] = [];

        // Use stored calendars if available and filtered by selection
        if (connection.calendars && Array.isArray(connection.calendars)) {
          // We have stored preferences - but check if they have backgroundColor
          const storedCalendars = connection.calendars as any[];
          const hasMissingColors = storedCalendars.some((c: any) => !c.backgroundColor);

          if (hasMissingColors) {
            // Refresh from API to get colors
            console.log('[CALENDAR] Stored calendars missing backgroundColor, refreshing from API...');
            const freshCalendars = await getGoogleCalendars(oauth2Client, accessToken);
            // Merge fresh data with stored selection preferences
            calendars = storedCalendars.map((stored: any) => {
              const fresh = freshCalendars.find(f => f.id === stored.id);
              return {
                ...stored,
                backgroundColor: fresh?.backgroundColor || stored.backgroundColor,
                summary: fresh?.summary || stored.title // Ensure summary is available
              };
            });
            console.log('[CALENDAR] Refreshed calendars with colors:', calendars.map(c => ({ id: c.id, bg: c.backgroundColor })));
          } else {
            calendars = storedCalendars;
          }

          calendarIds = calendars
            .filter((c: any) => c.selected !== false) // Include unless explicitly false
            .map((c: any) => c.id);
          console.log('[CALENDAR] Using calendars, selected:', calendarIds.length, 'of', calendars.length);
        } else {
          // Fallback: fetch all if no stored list (legacy behavior)
          console.log('[CALENDAR] No stored calendars, fetching from API...');
          calendars = await getGoogleCalendars(oauth2Client, accessToken);
          calendarIds = calendars.map(c => c.id);
          console.log('[CALENDAR] Fetched', calendars.length, 'calendars from API');
        }

        if (calendarIds.length === 0) {
          console.log('[CALENDAR] No calendars selected, skipping');
          continue;
        }

        // Fetch events for each calendar
        console.log('[CALENDAR] Fetching events from', calendarIds.length, 'calendars...');
        for (const calendarId of calendarIds) {
          try {
            console.log('[CALENDAR] Fetching events from calendar:', calendarId);
            const calendarEvents = await getGoogleEvents(
              oauth2Client,
              accessToken,
              calendarId,
              timeMin,
              timeMax
            );

            console.log('[CALENDAR] Found', calendarEvents.length, 'events in calendar:', calendarId);

            // Get calendar data for color fallback
            const calendarData = calendars.find(c => c.id === calendarId);

            events = events.concat(calendarEvents.map((event: any) => {
              const eventColor = event.colorId ? getGoogleEventColor(event.colorId) : calendarData?.backgroundColor;
              // Map Google reminders to our format
              const reminders: EventReminder[] | undefined = event.reminders?.overrides?.map((r: any) => ({
                method: r.method === 'email' ? 'email' : 'popup',
                minutes: r.minutes,
              })) || undefined;
              // Map Google transparency to busyStatus
              const busyStatus: BusyStatus | undefined = event.transparency === 'transparent' ? 'free'
                : event.transparency === 'opaque' ? 'busy' : undefined;
              return {
                id: event.id,
                title: event.summary || '(No Title)',
                description: event.description,
                start: event.start,
                end: event.end,
                location: event.location,
                recurringEventId: event.recurringEventId,
                isRecurring: !!event.recurringEventId,
                source: 'google' as const,
                calendarId,
                calendarTitle: calendarData?.summary || calendarData?.title || 'Google Calendar',
                backgroundColor: eventColor,
                reminders,
                busyStatus,
                visibility: event.visibility as EventVisibility || undefined,
                attendees: event.attendees?.map((a: any) => ({
                  email: a.email,
                  displayName: a.displayName,
                  responseStatus: a.responseStatus,
                })) as EventAttendee[] || undefined,
              };
            }));
          } catch (calError) {
            console.error(`[CALENDAR] Error fetching events from calendar ${calendarId}:`, calError);
            // Continue with other calendars
          }
        }
      } else if (connection.provider === 'apple') {
        const [email, appPassword] = connection.accessToken.split(':');

        if (!email || !appPassword) {
          console.error('[CALENDAR] Invalid Apple credentials format');
          continue;
        }

        // Use stored calendars from DB for filtering (avoids extra iCloud login)
        const storedCalendars = (connection.calendars && Array.isArray(connection.calendars) && connection.calendars.length > 0)
          ? (connection.calendars as any[]).filter((c: any) => c.selected !== false)
          : [];

        if (storedCalendars.length === 0) {
          console.log('[CALENDAR] Apple: no stored calendars, skipping (connect/refresh calendars in settings)');
          continue;
        }

        const calendarIds = storedCalendars.map((c: any) => c.id);

        // Single login: batch fetch events for all selected calendars
        console.log(`[CALENDAR] Apple: batch fetching ${calendarIds.length} calendars with single connection`);
        const batchResults = await getAppleEventsBatch(
          email,
          appPassword,
          calendarIds,
          timeMin,
          timeMax
        );

        for (const [calendarId, calendarEvents] of batchResults) {
          console.log(`[CALENDAR] Fetched ${calendarEvents.length} events from Apple calendar ${calendarId}`);
          const calMeta = storedCalendars.find((c: any) => c.id === calendarId);

          events = events.concat(calendarEvents.map((event: any) => {
            const isDateOnly = (s: string) => s && !s.includes('T');
            const startIsAllDay = isDateOnly(event.startDate);
            return {
              id: event.id,
              title: event.title,
              description: event.description,
              start: {
                dateTime: startIsAllDay ? undefined : event.startDate,
                date: startIsAllDay ? event.startDate : undefined,
              },
              end: {
                dateTime: startIsAllDay ? undefined : event.endDate,
                date: startIsAllDay ? event.endDate : undefined,
              },
              location: event.location,
              url: event.url,
              recurringEventId: event.recurringEventId,
              isRecurring: event.isRecurring,
              source: 'apple' as const,
              calendarId,
              calendarTitle: calMeta?.title || 'Apple Calendar',
              backgroundColor: calMeta?.color || '#FF3B30',
              reminders: event.reminders as EventReminder[] || undefined,
              busyStatus: event.busyStatus as BusyStatus || undefined,
              visibility: event.visibility as EventVisibility || undefined,
              attendees: event.attendees as EventAttendee[] || undefined,
              attachments: event.attachments as EventAttachment[] || undefined,
            };
          }));
        }
      } else if (connection.provider === 'synology') {
        const [username, password] = connection.accessToken.split(':');
        const serverUrl = connection.refreshToken;

        if (!username || !password || !serverUrl) {
          console.error('[CALENDAR] Invalid Synology credentials format');
          continue;
        }

        // Use stored calendars from DB (avoids extra Synology login)
        const storedCalendars = (connection.calendars && Array.isArray(connection.calendars) && connection.calendars.length > 0)
          ? (connection.calendars as any[]).filter((c: any) => c.selected !== false)
          : [];

        if (storedCalendars.length === 0) {
          console.log('[CALENDAR] Synology: no stored calendars, skipping');
          continue;
        }

        const calendarIds = storedCalendars.map((c: any) => c.id);

        // Single login: batch fetch events for all selected calendars
        console.log(`[CALENDAR] Synology: batch fetching ${calendarIds.length} calendars with single connection`);
        const batchResults = await getSynologyEventsBatch(
          serverUrl,
          username,
          password,
          calendarIds,
          timeMin,
          timeMax
        );

        for (const [calendarId, calendarEvents] of batchResults) {
          console.log(`[CALENDAR] Synology: fetched ${calendarEvents.length} events from calendar ${calendarId}`);
          const calMeta = storedCalendars.find((c: any) => c.id === calendarId);

          events = events.concat(calendarEvents.map((event: any) => {
            const isDateOnly = (s: string) => s && !s.includes('T');
            const startIsAllDay = isDateOnly(event.startDate);
            return {
              id: event.id,
              title: event.title,
              description: event.description,
              start: {
                dateTime: startIsAllDay ? undefined : event.startDate,
                date: startIsAllDay ? event.startDate : undefined,
              },
              end: {
                dateTime: startIsAllDay ? undefined : event.endDate,
                date: startIsAllDay ? event.endDate : undefined,
              },
              location: event.location,
              url: event.url,
              recurringEventId: event.recurringEventId,
              isRecurring: event.isRecurring,
              source: 'synology' as const,
              calendarId,
              calendarTitle: calMeta?.title || 'Synology Calendar',
              backgroundColor: calMeta?.backgroundColor || calMeta?.color || '#1b85ff',
              reminders: event.reminders as EventReminder[] || undefined,
              busyStatus: event.busyStatus as BusyStatus || undefined,
              visibility: event.visibility as EventVisibility || undefined,
              attendees: event.attendees as EventAttendee[] || undefined,
              attachments: event.attachments as EventAttachment[] || undefined,
            };
          }));
        }
      } else if (connection.provider === 'outlook') {
        console.log('[CALENDAR] Processing Outlook connection:', connection.id);

        if (isTokenExpired(connection.expiresAt)) {
          console.log('[CALENDAR] Outlook token expired, attempting refresh...');
          const newToken = await refreshOutlookToken(connection);
          if (newToken) {
            accessToken = newToken;
          } else {
            console.error('[CALENDAR] Failed to refresh Outlook token, skipping connection');
            continue;
          }
        }

        // Get calendars to fetch
        let calendarIds: string[] = [];
        let calendars: any[] = [];

        if (connection.calendars && Array.isArray(connection.calendars)) {
          calendars = connection.calendars as any[];
          calendarIds = calendars
            .filter((c: any) => c.selected !== false)
            .map((c: any) => c.id);
        }

        if (calendarIds.length > 0) {
          console.log('[CALENDAR] Fetching Outlook events from', calendarIds.length, 'calendars');
          for (const calendarId of calendarIds) {
            try {
              const outlookEvents = await getOutlookEvents(
                accessToken,
                calendarId,
                timeMin,
                timeMax
              );

              const calendarData = calendars.find(c => c.id === calendarId);

              console.log(`[CALENDAR] Outlook calendar ${calendarId}: ${outlookEvents.length} events`, outlookEvents.map((e: any) => ({ id: e.id?.slice(-10), title: e.summary, start: e.start?.dateTime?.slice(0, 16), recurring: e.isRecurring })));

              events = events.concat(outlookEvents.map((event: any) => ({
                id: event.id,
                title: event.summary || '(No Title)',
                description: event.description,
                start: event.start,
                end: event.end,
                location: event.location || '',
                recurringEventId: event.recurringEventId,
                isRecurring: event.isRecurring ?? false,
                source: 'outlook' as const,
                calendarId,
                calendarTitle: calendarData?.title || 'Outlook Calendar',
                backgroundColor: calendarData?.backgroundColor || calendarData?.color || '#0078d4',
                reminders: event.reminders as EventReminder[] || undefined,
                busyStatus: event.busyStatus as BusyStatus || undefined,
                visibility: event.visibility as EventVisibility || undefined,
                attendees: event.attendees as EventAttendee[] || undefined,
              })));

            } catch (calError) {
              console.error(`[CALENDAR] Error fetching Outlook events from ${calendarId}:`, calError);
            }
          }
        }
      } else if (connection.provider === 'notion') {
        console.log('[CALENDAR] Processing Notion connection:', connection.id);

        // Refresh token if available
        if (connection.refreshToken) {
          try {
            const refreshed = await refreshNotionToken(
              connection.refreshToken,
              process.env.NOTION_CLIENT_ID || '',
              process.env.NOTION_CLIENT_SECRET || '',
            );
            accessToken = refreshed.accessToken;
            await prisma.calendarConnection.update({
              where: { id: connection.id },
              data: {
                accessToken: refreshed.accessToken,
                refreshToken: refreshed.refreshToken,
                updatedAt: new Date(),
              },
            });
          } catch (refreshErr) {
            console.error('[CALENDAR] Notion token refresh failed, using existing token:', refreshErr);
          }
        }

        // Get selected databases
        const databases = (connection.calendars as any[] || []).filter((c: any) => c.selected !== false);
        if (databases.length === 0) {
          console.log('[CALENDAR] Notion: no databases selected, skipping');
        }

        for (const db of databases) {
          if (!db.dateProperty) {
            console.log(`[CALENDAR] Notion: database "${db.title}" has no date property, skipping`);
            continue;
          }

          try {
            const notionEvents = await getNotionEvents(
              accessToken,
              db.id,
              db.dateProperty,
              timeMin,
              timeMax,
              db.title,
            );

            console.log(`[CALENDAR] Notion: fetched ${notionEvents.length} events from "${db.title}"`);

            events = events.concat(notionEvents.map((ne) => ({
              id: ne.id,
              title: ne.title,
              description: ne.description,
              start: {
                dateTime: ne.allDay ? undefined : ne.start,
                date: ne.allDay ? ne.start : undefined,
              },
              end: {
                dateTime: ne.allDay ? undefined : (ne.end || ne.start),
                date: ne.allDay ? (ne.end || ne.start) : undefined,
              },
              location: undefined,
              url: ne.url,
              source: 'notion' as const,
              calendarId: db.id,
              calendarTitle: db.title,
              backgroundColor: '#000000',
            })));
          } catch (dbError) {
            console.error(`[CALENDAR] Notion: error fetching from "${db.title}":`, dbError);
          }
        }
      }

      console.log('[CALENDAR] Total events for connection:', events.length);
      allEvents.push(...events);
    } catch (error) {
      console.error(`[CALENDAR] Error fetching events from ${connection.provider} calendar:`, error);
      // Continue with other connections even if one fails
    }
  }

  console.log('[CALENDAR] Total events from all connections:', allEvents.length);
  return allEvents;
};

/**
 * Format event for display in the weekly view
 */
export const formatEventForDisplay = (event: CalendarEvent): CalendarEvent => {
  // Ensure consistent structure for display
  return {
    id: event.id,
    title: event.title,
    description: event.description,
    start: {
      dateTime: event.start.dateTime,
      date: event.start.date,
    },
    end: {
      dateTime: event.end.dateTime,
      date: event.end.date,
    },
    location: event.location,
    source: event.source,
    calendarId: event.calendarId,
    calendarTitle: event.calendarTitle,
  };
};

/**
 * Merge calendar events with existing tasks to avoid duplicates
 */
export const mergeWithTasks = (
  events: CalendarEvent[],
  tasks: Array<{ id: string; title: string }>
): CalendarEvent[] => {
  // Simple deduplication logic - if event title matches task title, consider it a duplicate
  const taskTitles = new Set(tasks.map(task => task.title.toLowerCase()));

  return events.filter(event => {
    // Only show events where the title doesn't match an existing task title
    return !taskTitles.has(event.title.toLowerCase());
  });
};

/**
 * Filter events by date range (week view)
 */
export const filterEventsByDateRange = (
  events: CalendarEvent[],
  startOfWeek: Date,
  endOfWeek: Date
): CalendarEvent[] => {
  const startTimestamp = startOfWeek.getTime();
  const endTimestamp = endOfWeek.getTime();

  return events.filter(event => {
    const eventStart = event.start.dateTime ? new Date(event.start.dateTime).getTime() :
      event.start.date ? new Date(event.start.date).getTime() : 0;

    return eventStart >= startTimestamp && eventStart <= endTimestamp;
  });
};

/**
 * Determine event visibility based on user settings
 */
export const determineEventVisibility = (
  event: CalendarEvent,
  userSettings: {
    showGoogleEvents?: boolean;
    showAppleEvents?: boolean;
    showPrivateEvents?: boolean;
  }
): boolean => {
  // Check if the user wants to see events from this provider
  if ((event.source === 'google' && !userSettings.showGoogleEvents) ||
    (event.source === 'apple' && !userSettings.showAppleEvents)) {
    return false;
  }

  // Check if private events should be shown (this is a simplified check)
  // In a real implementation, this would involve checking privacy settings
  if (!userSettings.showPrivateEvents) {
    // Simplified - in practice, private events would be filtered based on actual privacy flags
    return true;
  }

  return true;
};

/**
 * Create a new calendar event
 */
export const createCalendarEvent = async (
  connection: CalendarConnection,
  calendarId: string,
  event: Partial<CalendarEvent>
): Promise<CalendarEvent> => {
  if (connection.provider === 'google') {
    // Check key fields
    if (!event.title) throw new Error('Event title is required');
    if (!event.start || !event.end) throw new Error('Event start and end times are required');

    // Refresh token if needed
    let accessToken = connection.accessToken;
    if (isTokenExpired(connection.expiresAt)) {
      const newToken = await refreshGoogleToken(connection);
      if (newToken) accessToken = newToken;
      else throw new Error('Failed to refresh token');
    }

    const oauth2Client = initializeGoogleOAuth(
      process.env.GOOGLE_CLIENT_ID || '',
      process.env.GOOGLE_CLIENT_SECRET || '',
      process.env.GOOGLE_REDIRECT_URI || ''
    );

    // Map to Google format
    const rrule = toRRule(event.recurrence, event.recurrenceEndDate, event.recurrenceCount, event.recurrenceInterval, event.recurrenceDays);
    // Google requires timeZone on start/end when recurrence is present
    const timezone = event.timezone || Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';
    const startObj = rrule ? { ...event.start, timeZone: timezone } : event.start;
    const endObj = rrule ? { ...event.end, timeZone: timezone } : event.end;
    const googleEvent: any = {
      summary: event.title,
      description: event.description,
      start: startObj,
      end: endObj,
      location: event.location,
      ...(rrule ? { recurrence: [rrule] } : {}),
      ...(event.allDay ? { allDay: true } : {}),
      ...(event.url ? { source: { url: event.url, title: event.url } } : {}),
      ...(event.reminders?.length ? {
        reminders: { useDefault: false, overrides: event.reminders.map(r => ({ method: r.method === 'email' ? 'email' : 'popup', minutes: r.minutes })) }
      } : {}),
      ...(event.busyStatus ? { transparency: event.busyStatus === 'free' ? 'transparent' : 'opaque' } : {}),
      ...(event.visibility ? { visibility: event.visibility } : {}),
      ...(event.attendees?.length ? { attendees: event.attendees.map(a => ({ email: a.email, displayName: a.displayName })) } : {}),
    };

    const createdEvent = await import('./google-calendar').then(m =>
      m.createEvent(oauth2Client, accessToken, calendarId, googleEvent)
    );

    return {
      id: createdEvent.id,
      title: createdEvent.summary,
      description: createdEvent.description,
      start: createdEvent.start,
      end: createdEvent.end,
      location: createdEvent.location,
      source: 'google',
      calendarId,
      calendarTitle: '',
      isRecurring: !!event.recurrence,
      recurringEventId: event.recurrence ? createdEvent.id : undefined,
    } as CalendarEvent;
  } else if (connection.provider === 'outlook') {
    if (!event.title) throw new Error('Event title is required');
    if (!event.start || !event.end) throw new Error('Event start and end times are required');

    let accessToken = connection.accessToken;
    if (isTokenExpired(connection.expiresAt)) {
      const newToken = await refreshOutlookToken(connection);
      if (newToken) accessToken = newToken;
      else throw new Error('Failed to refresh token');
    }

    const startDate = event.start?.dateTime ? new Date(event.start.dateTime) : new Date();
    const outlookTz = event.timezone || Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';
    const outlookRecurrence = toOutlookRecurrence(event.recurrence, startDate, event.recurrenceEndDate, event.recurrenceCount, event.recurrenceInterval, event.recurrenceDays);
    console.log('[OUTLOOK] Creating event with recurrence:', JSON.stringify({ recurrence: event.recurrence, outlookRecurrence, outlookTz }));
    const createdEvent = await createOutlookEvent(accessToken, calendarId, {
      summary: event.title,
      description: event.description,
      start: { ...event.start, timeZone: outlookTz },
      end: { ...event.end, timeZone: outlookTz },
      location: event.location,
      allDay: event.allDay,
      recurrence: outlookRecurrence,
      reminders: event.reminders,
      busyStatus: event.busyStatus,
      visibility: event.visibility,
      attendees: event.attendees,
    });

    return {
      id: createdEvent.id,
      title: createdEvent.summary,
      description: createdEvent.description,
      start: createdEvent.start,
      end: createdEvent.end,
      location: createdEvent.location,
      allDay: createdEvent.allDay,
      source: 'outlook',
      calendarId,
      calendarTitle: '',
      isRecurring: !!event.recurrence,
      recurringEventId: event.recurrence ? createdEvent.id : undefined,
    } as CalendarEvent;
  } else if (connection.provider === 'apple') {
    const [email, appPassword] = connection.accessToken.split(':');

    // We need to map our event format to what createEvent expects
    // createEvent expects: { title, description?, location?, start: {dateTime?, date?}, end: ... }
    // Our 'event' arg is Partial<CalendarEvent>, which matches well.
    // However, event.start and event.end might be undefined in Partial, so we need checks.

    // Handle all-day event normalization for Apple (requires 'date' property, not 'dateTime')
    // AND exclusive end date (add 1 day if needed)
    const start = { ...event.start! };
    const end = { ...event.end! };

    if (event.allDay) {
      if (start.dateTime && !start.date) {
        start.date = start.dateTime.split('T')[0];
        delete start.dateTime;
      }
      if (end.dateTime && !end.date) {
        end.date = end.dateTime.split('T')[0];
        delete end.dateTime;
      }

      // Apple/iCloud requires DTEND to be the day AFTER the last day of the event
      if (start.date && end.date && start.date === end.date) {
        const d = new Date(end.date);
        d.setDate(d.getDate() + 1);
        end.date = d.toISOString().split('T')[0];
      }
    }

    const createdEvent = await import('./apple-calendar').then(m =>
      m.createEvent(email, appPassword, calendarId, {
        title: event.title!,
        description: stripHtmlForCalDav(event.description),
        location: event.location,
        url: event.url,
        recurrence: event.recurrence,
        recurrenceEndDate: event.recurrenceEndDate,
        recurrenceCount: event.recurrenceCount,
        recurrenceInterval: event.recurrenceInterval,
        recurrenceDays: event.recurrenceDays,
        timezone: event.timezone,
        start,
        end,
        reminders: event.reminders,
        attendees: event.attendees,
        attachments: event.attachments,
        busyStatus: event.busyStatus,
        visibility: event.visibility,
      })
    );

    return {
      id: createdEvent.id,
      title: createdEvent.title,
      description: createdEvent.description,
      start: { dateTime: createdEvent.startDate },
      end: { dateTime: createdEvent.endDate },
      location: createdEvent.location,
      source: 'apple',
      calendarId,
      calendarTitle: '',
      isRecurring: !!event.recurrence,
      recurringEventId: event.recurrence ? createdEvent.id : undefined,
    } as CalendarEvent;
  } else if (connection.provider === 'synology') {
    const [username, password] = connection.accessToken.split(':');
    const serverUrl = connection.refreshToken;

    if (!username || !password || !serverUrl) throw new Error('Invalid Synology credentials');
    if (!event.title) throw new Error('Event title is required');
    if (!event.start || !event.end) throw new Error('Event start and end times are required');

    const createdEvent = await import('./synology-calendar').then(m =>
      m.createEvent(serverUrl, username, password, calendarId, {
        title: event.title!,
        description: stripHtmlForCalDav(event.description),
        location: event.location,
        url: event.url,
        recurrence: event.recurrence,
        recurrenceEndDate: event.recurrenceEndDate,
        recurrenceCount: event.recurrenceCount,
        recurrenceInterval: event.recurrenceInterval,
        recurrenceDays: event.recurrenceDays,
        timezone: event.timezone,
        start: event.start!,
        end: event.end!,
        reminders: event.reminders,
        attendees: event.attendees,
        attachments: event.attachments,
        busyStatus: event.busyStatus,
        visibility: event.visibility,
      })
    );

    return {
      id: createdEvent.id,
      title: createdEvent.title,
      description: createdEvent.description,
      start: { dateTime: createdEvent.startDate },
      end: { dateTime: createdEvent.endDate },
      location: createdEvent.location,
      source: 'synology',
      calendarId,
      calendarTitle: '',
    } as CalendarEvent;
  } else if (connection.provider === 'notion') {
    if (!event.title) throw new Error('Event title is required');
    if (!event.start) throw new Error('Event start time is required');

    const databases = (connection.calendars as any[] || []);
    const db = databases.find((d: any) => d.id === calendarId);
    const dateProperty = db?.dateProperty;
    if (!dateProperty) throw new Error('No date property found for this Notion database');

    const startDate = event.start.dateTime || event.start.date || '';
    const endDate = event.end?.dateTime || event.end?.date || undefined;

    const { createEvent: createNotionEvent } = await import('./notion-calendar');
    const pageId = await createNotionEvent(
      connection.accessToken,
      calendarId,
      dateProperty,
      event.title,
      startDate,
      endDate,
    );

    return {
      id: pageId,
      title: event.title,
      start: event.start,
      end: event.end || event.start,
      source: 'notion',
      calendarId,
      calendarTitle: db?.title || '',
    } as CalendarEvent;
  }

  throw new Error(`Provider ${connection.provider} does not support creating events yet.`);
};

/**
 * Update an existing calendar event
 */
const stripHtmlForCalDav = (html: string | undefined): string | undefined => {
  if (!html) return html;
  return html.replace(/<[^>]*>/g, '').trim();
};

export const updateCalendarEvent = async (
  connection: CalendarConnection,
  calendarId: string,
  eventId: string,
  event: Partial<CalendarEvent>
): Promise<CalendarEvent> => {
  if (connection.provider === 'google') {
    // Refresh token if needed
    let accessToken = connection.accessToken;
    if (isTokenExpired(connection.expiresAt)) {
      const newToken = await refreshGoogleToken(connection);
      if (newToken) accessToken = newToken;
      else throw new Error('Failed to refresh token');
    }

    const oauth2Client = initializeGoogleOAuth(
      process.env.GOOGLE_CLIENT_ID || '',
      process.env.GOOGLE_CLIENT_SECRET || '',
      process.env.GOOGLE_REDIRECT_URI || ''
    );

    // Map to Google format
    const rrule = toRRule(event.recurrence, event.recurrenceEndDate, event.recurrenceCount, event.recurrenceInterval, event.recurrenceDays);
    const timezone = event.timezone || Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';
    const googleEvent: any = {};
    if (event.title !== undefined) googleEvent.summary = event.title;
    if (event.description !== undefined) googleEvent.description = event.description;
    if (event.start !== undefined) googleEvent.start = rrule ? { ...event.start, timeZone: timezone } : event.start;
    if (event.end !== undefined) googleEvent.end = rrule ? { ...event.end, timeZone: timezone } : event.end;
    if (event.location !== undefined) googleEvent.location = event.location;
    if (rrule) googleEvent.recurrence = [rrule];
    if (event.url) googleEvent.source = { url: event.url, title: event.url };
    if (event.reminders?.length) {
      googleEvent.reminders = { useDefault: false, overrides: event.reminders.map(r => ({ method: r.method === 'email' ? 'email' : 'popup', minutes: r.minutes })) };
    }
    if (event.busyStatus) googleEvent.transparency = event.busyStatus === 'free' ? 'transparent' : 'opaque';
    if (event.visibility) googleEvent.visibility = event.visibility;
    if (event.attendees) googleEvent.attendees = event.attendees.map(a => ({ email: a.email, displayName: a.displayName }));

    // Google adds _date suffix for instances. Editing base series only.
    const baseEventId = eventId.split('_')[0];

    const updatedEvent = await import('./google-calendar').then(m =>
      m.updateEvent(oauth2Client, accessToken, calendarId, baseEventId, googleEvent)
    );

    return {
      id: updatedEvent.id,
      title: updatedEvent.summary,
      description: updatedEvent.description,
      start: updatedEvent.start,
      end: updatedEvent.end,
      location: updatedEvent.location,
      source: 'google',
      calendarId,
      calendarTitle: '',
    } as CalendarEvent;
  } else if (connection.provider === 'outlook') {
    let accessToken = connection.accessToken;
    if (isTokenExpired(connection.expiresAt)) {
      const newToken = await refreshOutlookToken(connection);
      if (newToken) accessToken = newToken;
      else throw new Error('Failed to refresh token');
    }

    // Extract base series ID for Outlook
    const baseEventId = eventId.includes('::') ? eventId.split('::')[0] : eventId;
    console.log(`[OUTLOOK] Updating event: originalId=${eventId}, baseEventId=${baseEventId}`);
    console.log(`[OUTLOOK] Update payload: start=${JSON.stringify(event.start)}, end=${JSON.stringify(event.end)}, title=${event.title}`);

    const startDate = event.start?.dateTime ? new Date(event.start.dateTime) : new Date();
    const outlookUpdateTz = event.timezone || Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';
    const updatedEvent = await updateOutlookEvent(accessToken, calendarId, baseEventId, {
      summary: event.title,
      description: event.description,
      start: event.start ? { ...event.start, timeZone: outlookUpdateTz } : undefined,
      end: event.end ? { ...event.end, timeZone: outlookUpdateTz } : undefined,
      location: event.location,
      allDay: event.allDay,
      recurrence: toOutlookRecurrence(event.recurrence, startDate, event.recurrenceEndDate, event.recurrenceCount, event.recurrenceInterval, event.recurrenceDays),
      reminders: event.reminders,
      busyStatus: event.busyStatus,
      visibility: event.visibility,
      attendees: event.attendees,
    });

    return {
      id: updatedEvent.id,
      title: updatedEvent.summary,
      description: updatedEvent.description,
      start: updatedEvent.start,
      end: updatedEvent.end,
      location: updatedEvent.location,
      allDay: updatedEvent.allDay,
      source: 'outlook',
      calendarId,
      calendarTitle: '',
    } as CalendarEvent;
  } else if (connection.provider === 'apple') {
    const [email, appPassword] = connection.accessToken.split(':');

    // Normalize for Apple updates too
    if (event.allDay) {
      if (event.start && event.start.dateTime && !event.start.date) {
        event.start.date = event.start.dateTime.split('T')[0];
        delete event.start.dateTime;
      }
      if (event.end && event.end.dateTime && !event.end.date) {
        event.end.date = event.end.dateTime.split('T')[0];
        delete event.end.dateTime;
      }

      if (event.start && event.end && event.start.date && event.end.date && event.start.date === event.end.date) {
        const d = new Date(event.end.date);
        d.setDate(d.getDate() + 1);
        event.end.date = d.toISOString().split('T')[0];
      }
    }

    const updatedEvent = await import('./apple-calendar').then(m =>
      m.updateEvent(email, appPassword, calendarId, eventId, {
        title: event.title,
        description: stripHtmlForCalDav(event.description),
        location: event.location,
        url: event.url,
        start: event.start,
        end: event.end,
        reminders: event.reminders,
        attendees: event.attendees,
        attachments: event.attachments,
        busyStatus: event.busyStatus,
        visibility: event.visibility,
      })
    );

    return {
      id: updatedEvent.id,
      title: updatedEvent.title,
      description: updatedEvent.description,
      start: { dateTime: updatedEvent.startDate },
      end: { dateTime: updatedEvent.endDate },
      location: updatedEvent.location,
      url: updatedEvent.url,
      source: 'apple',
      calendarId,
      calendarTitle: '',
    } as CalendarEvent;
  } else if (connection.provider === 'synology') {
    const [username, password] = connection.accessToken.split(':');
    const serverUrl = connection.refreshToken;

    if (!username || !password || !serverUrl) throw new Error('Invalid Synology credentials');

    const updatedEvent = await import('./synology-calendar').then(m =>
      m.updateEvent(serverUrl, username, password, calendarId, eventId, {
        title: event.title,
        description: stripHtmlForCalDav(event.description),
        location: event.location,
        url: event.url,
        start: event.start,
        end: event.end,
        reminders: event.reminders,
        attendees: event.attendees,
        attachments: event.attachments,
        busyStatus: event.busyStatus,
        visibility: event.visibility,
      })
    );

    return {
      id: updatedEvent.id,
      title: updatedEvent.title,
      description: updatedEvent.description,
      start: { dateTime: updatedEvent.startDate },
      end: { dateTime: updatedEvent.endDate },
      location: updatedEvent.location,
      url: updatedEvent.url,
      source: 'synology',
      calendarId,
      calendarTitle: '',
    } as CalendarEvent;
  } else if (connection.provider === 'notion') {
    const databases = (connection.calendars as any[] || []);
    const db = databases.find((d: any) => d.id === calendarId);
    const dateProperty = db?.dateProperty;
    if (!dateProperty) throw new Error('No date property found for this Notion database');

    const startDate = event.start?.dateTime || event.start?.date || undefined;
    const endDate = event.end?.dateTime || event.end?.date || undefined;

    const { updateEvent: updateNotionEvent } = await import('./notion-calendar');
    await updateNotionEvent(
      connection.accessToken,
      eventId,
      dateProperty,
      event.title,
      startDate,
      endDate,
    );

    return {
      id: eventId,
      title: event.title || '',
      start: event.start || { dateTime: '' },
      end: event.end || event.start || { dateTime: '' },
      source: 'notion',
      calendarId,
      calendarTitle: db?.title || '',
    } as CalendarEvent;
  }

  throw new Error(`Provider ${connection.provider} does not support updating events yet.`);
};

/**
 * Delete a calendar event
 */
export const deleteCalendarEvent = async (
  connection: CalendarConnection,
  calendarId: string,
  eventId: string,
  deleteMode: string = 'all'
): Promise<void> => {
  if (connection.provider === 'google') {
    let accessToken = connection.accessToken;
    if (isTokenExpired(connection.expiresAt)) {
      const newToken = await refreshGoogleToken(connection);
      if (newToken) accessToken = newToken;
      else throw new Error('Failed to refresh token');
    }

    const oauth2Client = initializeGoogleOAuth(
      process.env.GOOGLE_CLIENT_ID || '',
      process.env.GOOGLE_CLIENT_SECRET || '',
      process.env.GOOGLE_REDIRECT_URI || ''
    );

    if (deleteMode === 'this') {
      // Delete the specific instance (use full instance ID with _suffix)
      await import('./google-calendar').then(m =>
        m.deleteEvent(oauth2Client, accessToken, calendarId, eventId)
      );
    } else if (deleteMode === 'past') {
      // Delete this instance and all past: delete the instance, then move series start forward
      const instanceDate = eventId.split('_')[1];
      const baseEventId = eventId.split('_')[0];
      if (instanceDate) {
        // Delete this specific instance
        try {
          await import('./google-calendar').then(m =>
            m.deleteEvent(oauth2Client, accessToken, calendarId, eventId)
          );
        } catch (e) {
          console.error('[DELETE] Failed to delete Google instance:', e);
        }
        // For Google, we can't easily move DTSTART, so fall back to deleting the series
        // and letting the calendarView only show future instances naturally
        // Actually, best approach: just delete all past instances individually is impractical.
        // Fall back to full series delete for now.
        console.warn('[DELETE] Google "this & past" not fully supported, deleted single instance only');
      } else {
        await import('./google-calendar').then(m =>
          m.deleteEvent(oauth2Client, accessToken, calendarId, baseEventId)
        );
      }
    } else if (deleteMode === 'future') {
      // Set UNTIL on the series to end before this instance
      const instanceDate = eventId.split('_')[1]; // e.g. "20260401T114000Z"
      if (instanceDate) {
        const baseEventId = eventId.split('_')[0];
        // Get the base event, modify its recurrence UNTIL
        const { getEvent, updateEvent } = await import('./google-calendar');
        try {
          const baseEvent = await getEvent(oauth2Client, accessToken, calendarId, baseEventId);
          if (baseEvent?.recurrence) {
            const updatedRecurrence = baseEvent.recurrence.map((rule: string) => {
              if (rule.startsWith('RRULE:')) {
                // Remove existing UNTIL/COUNT, add new UNTIL
                const cleaned = rule.replace(/;(UNTIL|COUNT)=[^;]*/g, '');
                return `${cleaned};UNTIL=${instanceDate.replace(/[-:]/g, '')}`;
              }
              return rule;
            });
            await updateEvent(oauth2Client, accessToken, calendarId, baseEventId, { recurrence: updatedRecurrence });
          }
        } catch (e) {
          console.error('[DELETE] Failed to modify Google series for future delete, falling back to full delete:', e);
          await import('./google-calendar').then(m =>
            m.deleteEvent(oauth2Client, accessToken, calendarId, baseEventId)
          );
        }
      } else {
        // Can't determine instance date, delete all
        const baseEventId = eventId.split('_')[0];
        await import('./google-calendar').then(m =>
          m.deleteEvent(oauth2Client, accessToken, calendarId, baseEventId)
        );
      }
    } else {
      // 'all' — delete the whole series
      const baseEventId = eventId.split('_')[0];
      await import('./google-calendar').then(m =>
        m.deleteEvent(oauth2Client, accessToken, calendarId, baseEventId)
      );
    }
    return;
  } else if (connection.provider === 'outlook') {
    let accessToken = connection.accessToken;
    if (isTokenExpired(connection.expiresAt)) {
      const newToken = await refreshOutlookToken(connection);
      if (newToken) accessToken = newToken;
      else throw new Error('Failed to refresh token');
    }

    // Outlook event ID format for recurring: "seriesMasterId::instanceId"
    const hasInstanceId = eventId.includes('::');
    const seriesMasterId = hasInstanceId ? eventId.split('::')[0] : eventId;
    const instanceId = hasInstanceId ? eventId.split('::')[1] : eventId;

    if (deleteMode === 'this' && hasInstanceId) {
      // Delete just this specific occurrence
      await deleteOutlookEvent(accessToken, calendarId, instanceId);
    } else if (deleteMode === 'all' || !hasInstanceId) {
      // Delete the entire series (use series master ID)
      await deleteOutlookEvent(accessToken, calendarId, seriesMasterId);
    } else {
      // 'future' or 'past' — Outlook doesn't support partial series delete easily
      // Fall back to deleting the series
      await deleteOutlookEvent(accessToken, calendarId, seriesMasterId);
    }
    return;
  } else if (connection.provider === 'apple') {
    const [email, appPassword] = connection.accessToken.split(':');

    if (deleteMode === 'this' || deleteMode === 'future' || deleteMode === 'past') {
      await import('./apple-calendar').then(m =>
        m.deleteRecurringInstance(email, appPassword, calendarId, eventId, deleteMode)
      );
    } else {
      await import('./apple-calendar').then(m =>
        m.deleteEvent(email, appPassword, calendarId, eventId)
      );
    }
    return;
  } else if (connection.provider === 'synology') {
    const [username, password] = connection.accessToken.split(':');
    const serverUrl = connection.refreshToken;

    if (!username || !password || !serverUrl) throw new Error('Invalid Synology credentials');

    if (deleteMode === 'this' || deleteMode === 'future' || deleteMode === 'past') {
      await import('./synology-calendar').then(m =>
        m.deleteRecurringInstance(serverUrl, username, password, calendarId, eventId, deleteMode)
      );
    } else {
      await import('./synology-calendar').then(m =>
        m.deleteEvent(serverUrl, username, password, calendarId, eventId)
      );
    }
    return;
  } else if (connection.provider === 'notion') {
    const { deleteEvent: deleteNotionEvent } = await import('./notion-calendar');
    await deleteNotionEvent(connection.accessToken, eventId);
    return;
  }

  throw new Error(`Provider ${connection.provider} does not support deleting events yet.`);
};