/**
 * Mawidi Demo Scheduler - Outlook Calendar Integration (Boilerplate)
 * Microsoft Graph API integration for calendar availability checking
 *
 * NOTE: This is a boilerplate implementation for future integration.
 * Requires Azure AD app registration and authentication setup.
 */

import type { TimeSlot, DateRange } from "../types/scheduler.types";

/**
 * Configuration for Outlook integration
 * These should be set via environment variables in production
 */
export const OUTLOOK_CONFIG = {
  clientId: process.env.NEXT_PUBLIC_AZURE_CLIENT_ID || "",
  tenantId: process.env.NEXT_PUBLIC_AZURE_TENANT_ID || "",
  redirectUri: process.env.NEXT_PUBLIC_AZURE_REDIRECT_URI || "",
  scopes: ["Calendars.Read", "Calendars.ReadWrite"],
  graphApiEndpoint: "https://graph.microsoft.com/v1.0",
};

/**
 * Outlook calendar event interface
 */
export interface OutlookCalendarEvent {
  id: string;
  subject: string;
  start: {
    dateTime: string;
    timeZone: string;
  };
  end: {
    dateTime: string;
    timeZone: string;
  };
  isAllDay: boolean;
  isCancelled: boolean;
  showAs:
    | "free"
    | "tentative"
    | "busy"
    | "oof"
    | "workingElsewhere"
    | "unknown";
}

/**
 * Outlook Calendar Service
 * Handles all interactions with Microsoft Graph API
 */
export class OutlookCalendarService {
  private accessToken: string | null = null;

  /**
   * Initialize the service with an access token
   *
   * @param accessToken - Microsoft Graph API access token
   */
  constructor(accessToken?: string) {
    this.accessToken = accessToken || null;
  }

  /**
   * Set access token for API calls
   *
   * @param token - Access token
   */
  setAccessToken(token: string): void {
    this.accessToken = token;
  }

  /**
   * Get calendar events within a date range
   *
   * @param startDate - Start of range
   * @param endDate - End of range
   * @returns Array of calendar events
   */
  async getCalendarEvents(
    startDate: Date,
    endDate: Date,
  ): Promise<OutlookCalendarEvent[]> {
    if (!this.accessToken) {
      throw new Error("Access token not set. Please authenticate first.");
    }

    const startDateTime = startDate.toISOString();
    const endDateTime = endDate.toISOString();

    try {
      const response = await fetch(
        `${OUTLOOK_CONFIG.graphApiEndpoint}/me/calendarview?startDateTime=${startDateTime}&endDateTime=${endDateTime}`,
        {
          method: "GET",
          headers: {
            Authorization: `Bearer ${this.accessToken}`,
            "Content-Type": "application/json",
            Prefer: 'outlook.timezone="UTC"',
          },
        },
      );

      if (!response.ok) {
        throw new Error(`Outlook API error: ${response.statusText}`);
      }

      const data = await response.json();
      return data.value as OutlookCalendarEvent[];
    } catch (error) {
      console.error("Failed to fetch calendar events:", error);
      throw error;
    }
  }

  /**
   * Check if a specific time slot conflicts with existing events
   *
   * @param dateTime - Start of time slot
   * @param duration - Duration in minutes
   * @returns True if slot is available
   */
  async checkAvailability(dateTime: Date, duration: number): Promise<boolean> {
    const endDateTime = new Date(dateTime.getTime() + duration * 60 * 1000);

    try {
      const events = await this.getCalendarEvents(dateTime, endDateTime);

      // Filter out cancelled events and check for conflicts
      const conflicts = events.filter(
        (event) =>
          !event.isCancelled && event.showAs !== "free" && !event.isAllDay,
      );

      return conflicts.length === 0;
    } catch (error) {
      console.error("Failed to check availability:", error);
      // Return false (unavailable) on error to be safe
      return false;
    }
  }

  /**
   * Get all booked time slots for a specific date
   *
   * @param date - Date to check
   * @returns Array of booked time ranges
   */
  async getBookedSlots(date: Date): Promise<DateRange[]> {
    const startOfDay = new Date(date);
    startOfDay.setHours(0, 0, 0, 0);

    const endOfDay = new Date(date);
    endOfDay.setHours(23, 59, 59, 999);

    try {
      const events = await this.getCalendarEvents(startOfDay, endOfDay);

      return events
        .filter(
          (event) =>
            !event.isCancelled && event.showAs !== "free" && !event.isAllDay,
        )
        .map((event) => ({
          start: new Date(event.start.dateTime),
          end: new Date(event.end.dateTime),
        }));
    } catch (error) {
      console.error("Failed to get booked slots:", error);
      return [];
    }
  }

  /**
   * Create a new calendar event (for confirmed bookings)
   *
   * @param event - Event details
   * @returns Created event ID
   */
  async createCalendarEvent(event: {
    subject: string;
    start: Date;
    end: Date;
    attendees?: string[];
    body?: string;
    location?: string;
  }): Promise<string> {
    if (!this.accessToken) {
      throw new Error("Access token not set. Please authenticate first.");
    }

    const eventData = {
      subject: event.subject,
      start: {
        dateTime: event.start.toISOString(),
        timeZone: "UTC",
      },
      end: {
        dateTime: event.end.toISOString(),
        timeZone: "UTC",
      },
      attendees: event.attendees?.map((email) => ({
        emailAddress: {
          address: email,
        },
        type: "required",
      })),
      body: {
        contentType: "HTML",
        content: event.body || "",
      },
      location: event.location
        ? {
            displayName: event.location,
          }
        : undefined,
      isOnlineMeeting: true, // Enable Teams meeting
      onlineMeetingProvider: "teamsForBusiness",
    };

    try {
      const response = await fetch(
        `${OUTLOOK_CONFIG.graphApiEndpoint}/me/events`,
        {
          method: "POST",
          headers: {
            Authorization: `Bearer ${this.accessToken}`,
            "Content-Type": "application/json",
          },
          body: JSON.stringify(eventData),
        },
      );

      if (!response.ok) {
        throw new Error(`Failed to create event: ${response.statusText}`);
      }

      const createdEvent = await response.json();
      return createdEvent.id;
    } catch (error) {
      console.error("Failed to create calendar event:", error);
      throw error;
    }
  }

  /**
   * Update an existing calendar event
   *
   * @param eventId - Event ID to update
   * @param updates - Updated event data
   */
  async updateCalendarEvent(
    eventId: string,
    updates: Partial<OutlookCalendarEvent>,
  ): Promise<void> {
    if (!this.accessToken) {
      throw new Error("Access token not set. Please authenticate first.");
    }

    try {
      const response = await fetch(
        `${OUTLOOK_CONFIG.graphApiEndpoint}/me/events/${eventId}`,
        {
          method: "PATCH",
          headers: {
            Authorization: `Bearer ${this.accessToken}`,
            "Content-Type": "application/json",
          },
          body: JSON.stringify(updates),
        },
      );

      if (!response.ok) {
        throw new Error(`Failed to update event: ${response.statusText}`);
      }
    } catch (error) {
      console.error("Failed to update calendar event:", error);
      throw error;
    }
  }

  /**
   * Cancel a calendar event
   *
   * @param eventId - Event ID to cancel
   * @param comment - Optional cancellation comment
   */
  async cancelCalendarEvent(eventId: string, comment?: string): Promise<void> {
    if (!this.accessToken) {
      throw new Error("Access token not set. Please authenticate first.");
    }

    try {
      const response = await fetch(
        `${OUTLOOK_CONFIG.graphApiEndpoint}/me/events/${eventId}/cancel`,
        {
          method: "POST",
          headers: {
            Authorization: `Bearer ${this.accessToken}`,
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            comment: comment || "Meeting cancelled",
          }),
        },
      );

      if (!response.ok) {
        throw new Error(`Failed to cancel event: ${response.statusText}`);
      }
    } catch (error) {
      console.error("Failed to cancel calendar event:", error);
      throw error;
    }
  }
}

/**
 * Helper function to filter available time slots based on Outlook calendar
 *
 * @param timeSlots - Array of time slots to filter
 * @param outlookService - Outlook service instance
 * @returns Filtered time slots with availability
 */
export async function filterSlotsByOutlookAvailability(
  timeSlots: TimeSlot[],
  outlookService: OutlookCalendarService,
): Promise<TimeSlot[]> {
  const filteredSlots = await Promise.all(
    timeSlots.map(async (slot) => {
      if (!slot.available) {
        return slot; // Already marked unavailable
      }

      try {
        const slotDateTime = new Date(slot.dateTime);
        const duration = 30; // Default duration for checking

        const isAvailable = await outlookService.checkAvailability(
          slotDateTime,
          duration,
        );

        return {
          ...slot,
          available: isAvailable,
          unavailableReason: isAvailable ? undefined : ("booked" as const),
        };
      } catch {
        // On error, mark as unavailable to be safe
        return {
          ...slot,
          available: false,
          unavailableReason: "outside-hours" as const,
        };
      }
    }),
  );

  return filteredSlots;
}

/**
 * Initialize Outlook authentication (MSAL.js)
 * This is a placeholder for the actual MSAL implementation
 *
 * @returns Promise that resolves to access token
 */
export async function initializeOutlookAuth(): Promise<string> {
  // TODO: Implement MSAL.js authentication flow
  // This requires:
  // 1. Azure AD app registration
  // 2. MSAL.js library installation
  // 3. Proper redirect URI configuration
  // 4. User consent flow

  throw new Error(
    "Outlook authentication not implemented. Please set up Azure AD app registration and MSAL.js",
  );
}

/**
 * Check if Outlook integration is configured
 *
 * @returns True if all required config is present
 */
export function isOutlookConfigured(): boolean {
  return !!(
    OUTLOOK_CONFIG.clientId &&
    OUTLOOK_CONFIG.tenantId &&
    OUTLOOK_CONFIG.redirectUri
  );
}

/**
 * Export for future use
 */
export default OutlookCalendarService;
