
/**
 * Calendar Sync Service
 * Handles 2-way sync between CRM and Google Calendar / Microsoft Outlook
 * 
 * Phase 1: ICS Export (One-way sync) ✅
 * Phase 2: Full 2-way sync with conflict resolution ✅
 */

import { google } from 'googleapis';
import { Client } from '@microsoft/microsoft-graph-client';
import { ConfidentialClientApplication } from '@azure/msal-node';
import { prisma } from '@/lib/db';

// Google Calendar Integration
export async function syncToGoogleCalendar(
  accessToken: string,
  event: any
): Promise<string> {
  const oauth2Client = new google.auth.OAuth2();
  oauth2Client.setCredentials({ access_token: accessToken });

  const calendar = google.calendar({ version: 'v3', auth: oauth2Client });

  const googleEvent = {
    summary: event.title,
    description: event.description,
    location: event.location,
    start: {
      dateTime: event.startTime,
      timeZone: 'Africa/Johannesburg'
    },
    end: {
      dateTime: event.endTime,
      timeZone: 'Africa/Johannesburg'
    },
    reminders: {
      useDefault: false,
      overrides: [
        { method: 'email', minutes: event.reminderMinutes || 60 },
        { method: 'popup', minutes: event.reminderMinutes || 60 }
      ]
    }
  };

  try {
    const response = await calendar.events.insert({
      calendarId: 'primary',
      requestBody: googleEvent
    });

    return response.data.id || '';
  } catch (error) {
    console.error('Error syncing to Google Calendar:', error);
    throw error;
  }
}

// Microsoft Outlook Integration
export async function syncToMicrosoftCalendar(
  accessToken: string,
  event: any
): Promise<string> {
  const client = Client.init({
    authProvider: (done) => {
      done(null, accessToken);
    }
  });

  const outlookEvent = {
    subject: event.title,
    body: {
      contentType: 'HTML',
      content: event.description || ''
    },
    location: {
      displayName: event.location || ''
    },
    start: {
      dateTime: event.startTime,
      timeZone: 'Africa/Johannesburg'
    },
    end: {
      dateTime: event.endTime,
      timeZone: 'Africa/Johannesburg'
    },
    reminderMinutesBeforeStart: event.reminderMinutes || 60
  };

  try {
    const response = await client.api('/me/events').post(outlookEvent);
    return response.id;
  } catch (error) {
    console.error('Error syncing to Microsoft Calendar:', error);
    throw error;
  }
}

// Update event in external calendar
export async function updateExternalEvent(
  provider: 'GOOGLE' | 'MICROSOFT',
  accessToken: string,
  eventId: string,
  event: any
): Promise<void> {
  if (provider === 'GOOGLE') {
    const oauth2Client = new google.auth.OAuth2();
    oauth2Client.setCredentials({ access_token: accessToken });
    const calendar = google.calendar({ version: 'v3', auth: oauth2Client });

    await calendar.events.update({
      calendarId: 'primary',
      eventId: eventId,
      requestBody: {
        summary: event.title,
        description: event.description,
        location: event.location,
        start: {
          dateTime: event.startTime,
          timeZone: 'Africa/Johannesburg'
        },
        end: {
          dateTime: event.endTime,
          timeZone: 'Africa/Johannesburg'
        }
      }
    });
  } else {
    const client = Client.init({
      authProvider: (done) => done(null, accessToken)
    });

    await client.api(`/me/events/${eventId}`).update({
      subject: event.title,
      body: {
        contentType: 'HTML',
        content: event.description || ''
      },
      location: {
        displayName: event.location || ''
      },
      start: {
        dateTime: event.startTime,
        timeZone: 'Africa/Johannesburg'
      },
      end: {
        dateTime: event.endTime,
        timeZone: 'Africa/Johannesburg'
      }
    });
  }
}

// Delete event from external calendar
export async function deleteExternalEvent(
  provider: 'GOOGLE' | 'MICROSOFT',
  accessToken: string,
  eventId: string
): Promise<void> {
  if (provider === 'GOOGLE') {
    const oauth2Client = new google.auth.OAuth2();
    oauth2Client.setCredentials({ access_token: accessToken });
    const calendar = google.calendar({ version: 'v3', auth: oauth2Client });

    await calendar.events.delete({
      calendarId: 'primary',
      eventId: eventId
    });
  } else {
    const client = Client.init({
      authProvider: (done) => done(null, accessToken)
    });

    await client.api(`/me/events/${eventId}`).delete();
  }
}

// Fetch events from external calendar (for 2-way sync)
export async function fetchExternalEvents(
  provider: 'GOOGLE' | 'MICROSOFT',
  accessToken: string,
  startDate: Date,
  endDate: Date
): Promise<any[]> {
  if (provider === 'GOOGLE') {
    const oauth2Client = new google.auth.OAuth2();
    oauth2Client.setCredentials({ access_token: accessToken });
    const calendar = google.calendar({ version: 'v3', auth: oauth2Client });

    const response = await calendar.events.list({
      calendarId: 'primary',
      timeMin: startDate.toISOString(),
      timeMax: endDate.toISOString(),
      singleEvents: true,
      orderBy: 'startTime'
    });

    return (response.data.items || []).map((event) => ({
      externalId: event.id,
      title: event.summary,
      description: event.description,
      location: event.location,
      startTime: event.start?.dateTime || event.start?.date,
      endTime: event.end?.dateTime || event.end?.date,
      allDay: !event.start?.dateTime
    }));
  } else {
    const client = Client.init({
      authProvider: (done) => done(null, accessToken)
    });

    const response = await client
      .api('/me/calendarView')
      .query({
        startDateTime: startDate.toISOString(),
        endDateTime: endDate.toISOString()
      })
      .get();

    return (response.value || []).map((event: any) => ({
      externalId: event.id,
      title: event.subject,
      description: event.body?.content,
      location: event.location?.displayName,
      startTime: event.start?.dateTime,
      endTime: event.end?.dateTime,
      allDay: event.isAllDay
    }));
  }
}

// Detect and resolve conflicts for 2-way sync
export async function detectConflicts(
  userId: string,
  externalEvents: any[],
  crmEvents: any[]
): Promise<any[]> {
  const conflicts = [];

  for (const externalEvent of externalEvents) {
    // Check for time conflicts with CRM events
    for (const crmEvent of crmEvents) {
      const externalStart = new Date(externalEvent.startTime);
      const externalEnd = new Date(externalEvent.endTime);
      const crmStart = new Date(crmEvent.startTime);
      const crmEnd = new Date(crmEvent.endTime);

      // Check if times overlap
      if (
        (externalStart < crmEnd && externalEnd > crmStart) ||
        (crmStart < externalEnd && crmEnd > externalStart)
      ) {
        conflicts.push({
          type: 'TIME_CONFLICT',
          externalEvent,
          crmEvent,
          resolution: 'CRM_WINS' // CRM is source of truth
        });
      }
    }
  }

  return conflicts;
}

// Refresh Google OAuth token
export async function refreshGoogleToken(
  refreshToken: string,
  userId: string
): Promise<string> {
  try {
    const oauth2Client = new google.auth.OAuth2(
      process.env.GOOGLE_CLIENT_ID,
      process.env.GOOGLE_CLIENT_SECRET,
      `${process.env.NEXTAUTH_URL}/api/calendar/oauth/google/callback`
    );

    oauth2Client.setCredentials({ refresh_token: refreshToken });

    const { credentials } = await oauth2Client.refreshAccessToken();
    const newAccessToken = credentials.access_token;
    const newExpiryDate = credentials.expiry_date
      ? new Date(credentials.expiry_date)
      : new Date(Date.now() + 3600 * 1000);

    if (!newAccessToken) {
      throw new Error('Failed to refresh token');
    }

    // Update token in database
    await prisma.calendarSync.update({
      where: { userId },
      data: {
        accessToken: newAccessToken,
        tokenExpiry: newExpiryDate,
        refreshToken: credentials.refresh_token || refreshToken,
      },
    });

    return newAccessToken;
  } catch (error) {
    console.error('Error refreshing Google token:', error);
    throw error;
  }
}

// Refresh Microsoft OAuth token
// Note: Microsoft MSAL handles refresh tokens internally via a token cache
// For production, implement proper token cache persistence
export async function refreshMicrosoftToken(
  refreshToken: string,
  userId: string
): Promise<string> {
  try {
    // For Microsoft OAuth, we need users to re-authenticate if token expires
    // MSAL doesn't expose refresh tokens in the same way as Google
    // In production, implement a proper MSAL token cache
    
    throw new Error('Microsoft token expired. Please reconnect your calendar in sync settings.');
  } catch (error) {
    console.error('Error refreshing Microsoft token:', error);
    
    // Update sync status to indicate reconnection needed
    await prisma.calendarSync.update({
      where: { userId },
      data: {
        isEnabled: false,
        lastSyncStatus: 'TOKEN_EXPIRED',
      },
    }).catch(() => {});
    
    throw error;
  }
}
