// @ts-nocheck
/* eslint-disable @typescript-eslint/no-explicit-any */
// import { GoogleCalendarEvent as CalendarEvent } from './google-calendar';

export interface OutlookCalendar {
    id: string;
    name: string;
    isDefaultCalendar: boolean;
    canEdit: boolean;
    color?: string;
    hexColor?: string;
    owner: {
        name: string;
        address: string;
    };
}

// Microsoft Graph API calendar color names → hex values
const OUTLOOK_COLOR_MAP: Record<string, string> = {
    auto: '#0078d4',
    lightBlue: '#69afe5',
    lightGreen: '#7bd148',
    lightOrange: '#ffb878',
    lightGray: '#b3b3b3',
    lightYellow: '#fbd75b',
    lightTeal: '#92e1c0',
    lightPink: '#f691b2',
    lightBrown: '#c2a282',
    lightRed: '#ff887c',
    maxColor: '#0078d4',
};

const GRAPH_ENDPOINT = 'https://graph.microsoft.com/v1.0';
const REDIRECT_URI = process.env.MICROSOFT_REDIRECT_URI || `${process.env.NEXTAUTH_URL}/api/calendar/outlook/callback`;

/**
 * Generate OAuth2 Authorization URL
 */
export const getAuthUrl = () => {
    const tenant = 'common';
    const clientId = process.env.MICROSOFT_CLIENT_ID;

    if (!clientId) throw new Error('MICROSOFT_CLIENT_ID is not defined');

    const scopes = [
        'offline_access',
        'user.read',
        'Calendars.ReadWrite',
        'Tasks.ReadWrite'
    ].join(' ');

    const params = new URLSearchParams({
        client_id: clientId,
        response_type: 'code',
        redirect_uri: REDIRECT_URI,
        response_mode: 'query',
        scope: scopes,
        state: 'outlook-auth' // Can be random for security
    });

    return `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/authorize?${params.toString()}`;
};

/**
 * Exchange Authorization Code for Tokens
 */
export const getTokens = async (code: string) => {
    const tenant = 'common';
    const clientId = process.env.MICROSOFT_CLIENT_ID;
    const clientSecret = process.env.MICROSOFT_CLIENT_SECRET;

    if (!clientId || !clientSecret) throw new Error('Microsoft credentials not defined');

    const params = new URLSearchParams({
        client_id: clientId,
        scope: 'offline_access user.read Calendars.ReadWrite Tasks.ReadWrite',
        code: code,
        redirect_uri: REDIRECT_URI,
        grant_type: 'authorization_code',
        client_secret: clientSecret
    });

    const response = await fetch(`https://login.microsoftonline.com/${tenant}/oauth2/v2.0/token`, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
        },
        body: params.toString()
    });

    if (!response.ok) {
        const error = await response.text();
        console.error('Error getting Outlook tokens:', error);
        throw new Error(`Failed to get tokens: ${response.statusText}`);
    }

    return response.json();
};

/**
 * Refresh Access Token
 */
export const refreshAccessToken = async (refreshToken: string) => {
    const tenant = 'common';
    const clientId = process.env.MICROSOFT_CLIENT_ID;
    const clientSecret = process.env.MICROSOFT_CLIENT_SECRET;

    if (!clientId || !clientSecret) throw new Error('Microsoft credentials not defined');

    const params = new URLSearchParams({
        client_id: clientId,
        scope: 'offline_access user.read Calendars.ReadWrite Tasks.ReadWrite',
        refresh_token: refreshToken,
        redirect_uri: REDIRECT_URI,
        grant_type: 'refresh_token',
        client_secret: clientSecret
    });

    const response = await fetch(`https://login.microsoftonline.com/${tenant}/oauth2/v2.0/token`, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
        },
        body: params.toString()
    });

    if (!response.ok) {
        const error = await response.text();
        console.error('Error refreshing Outlook token:', error);
        throw new Error(`Failed to refresh token: ${response.statusText}`);
    }

    return response.json();
};

/**
 * Get User's Calendars
 */
export const getUserCalendars = async (accessToken: string): Promise<OutlookCalendar[]> => {
    const response = await fetch(`${GRAPH_ENDPOINT}/me/calendars`, {
        headers: {
            'Authorization': `Bearer ${accessToken}`,
            'Prefer': 'outlook.timezone="UTC"'
        }
    });

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

    const data = await response.json();
    return (data.value || []).map((cal: any) => ({
        ...cal,
        hexColor: cal.hexColor || OUTLOOK_COLOR_MAP[cal.color] || OUTLOOK_COLOR_MAP['auto'],
    }));
};

/**
 * Get Upcoming Events
 */
export const getUpcomingEvents = async (
    accessToken: string,
    calendarId: string,
    startDateTime: string,
    endDateTime: string
) => {
    const params = new URLSearchParams({
        startDateTime: startDateTime,
        endDateTime: endDateTime,
        '$select': 'subject,bodyPreview,start,end,location,webLink,isAllDay,seriesMasterId,type,reminderMinutesBeforeStart,isReminderOn,showAs,sensitivity,attendees',
        '$orderby': 'start/dateTime',
        '$top': '50'
    });

    let response: Response | null = null;
    for (let attempt = 0; attempt < 3; attempt++) {
        response = await fetch(
            `${GRAPH_ENDPOINT}/me/calendars/${encodeURIComponent(calendarId)}/calendarView?${params.toString()}`,
            {
                headers: {
                    'Authorization': `Bearer ${accessToken}`,
                    'Prefer': 'outlook.timezone="UTC"'
                }
            }
        );

        if (response.status === 429) {
            const retryAfter = parseInt(response.headers.get('Retry-After') || '', 10);
            const delay = (retryAfter > 0 ? retryAfter : (attempt + 1) * 2) * 1000;
            console.warn(`[OUTLOOK] 429 throttled for calendar ${calendarId}, retrying in ${delay}ms (attempt ${attempt + 1}/3)`);
            await new Promise(r => setTimeout(r, delay));
            continue;
        }
        break;
    }

    if (!response!.ok) {
        const errorText = await response!.text();
        console.error(`[OUTLOOK] Failed to fetch events for calendar ${calendarId}:`, response!.status, response!.statusText, errorText);
        throw new Error(`Failed to fetch events: ${response!.status} ${response!.statusText}`);
    }

    const data = await response.json();
    return data.value.map((event: any) => mapOutlookEventResponse(event));
};

const ensureTimeZone = (dateTimeObj: any) => {
    if (!dateTimeObj) return dateTimeObj;
    const tz = dateTimeObj.timeZone || 'UTC';
    let dt = dateTimeObj.dateTime;
    // If dateTime is UTC (ends with Z) but we have a real timezone, convert to local representation
    if (dt && typeof dt === 'string' && dt.endsWith('Z') && tz !== 'UTC') {
        const d = new Date(dt);
        // Format as local time in the target timezone: YYYY-MM-DDTHH:mm:ss.0000000
        const parts = new Intl.DateTimeFormat('en-CA', {
            timeZone: tz, year: 'numeric', month: '2-digit', day: '2-digit',
            hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false
        }).formatToParts(d);
        const get = (type: string) => parts.find(p => p.type === type)?.value || '00';
        dt = `${get('year')}-${get('month')}-${get('day')}T${get('hour')}:${get('minute')}:${get('second')}.0000000`;
    }
    return { dateTime: dt, timeZone: tz };
};

/**
 * Create Outlook Event
 */
// Normalize Outlook response dateTime: ensure UTC dates have Z suffix
const normalizeOutlookDateTime = (dtObj: any) => {
    if (!dtObj?.dateTime) return dtObj;
    let dt = dtObj.dateTime;
    // Outlook returns UTC datetimes without Z suffix — append it
    if (dt && typeof dt === 'string' && dtObj.timeZone === 'UTC' && !dt.endsWith('Z')) {
        dt = dt + 'Z';
    }
    return { dateTime: dt, timeZone: dtObj.timeZone };
};

// Map a raw Microsoft Graph event response into our internal shape.
// Used after create/update to keep the full set of fields (busyStatus, visibility,
// attendees, reminders, recurrence info) flowing back to the cache + UI.
const mapOutlookEventResponse = (ev: any) => {
    const showAsMap: Record<string, string> = {
        'free': 'free', 'tentative': 'tentative', 'busy': 'busy',
        'oof': 'oof', 'workingElsewhere': 'workingElsewhere',
    };
    const sensitivityMap: Record<string, string> = {
        'normal': 'default', 'personal': 'default',
        'private': 'private', 'confidential': 'confidential',
    };
    return {
        id: ev.seriesMasterId ? `${ev.seriesMasterId}::${ev.id}` : ev.id,
        summary: ev.subject,
        description: ev.body?.content || ev.bodyPreview,
        start: normalizeOutlookDateTime(ev.start),
        end: normalizeOutlookDateTime(ev.end),
        location: ev.location?.displayName,
        htmlLink: ev.webLink,
        allDay: ev.isAllDay,
        recurringEventId: ev.seriesMasterId || undefined,
        isRecurring: ev.type === 'occurrence' || ev.type === 'exception' || ev.type === 'seriesMaster',
        reminders: ev.isReminderOn && ev.reminderMinutesBeforeStart != null
            ? [{ method: 'popup', minutes: ev.reminderMinutesBeforeStart }]
            : undefined,
        busyStatus: showAsMap[ev.showAs] || undefined,
        visibility: sensitivityMap[ev.sensitivity] || undefined,
        attendees: ev.attendees?.map((a: any) => ({
            email: a.emailAddress?.address,
            displayName: a.emailAddress?.name,
            responseStatus: a.status?.response === 'accepted' ? 'accepted'
                : a.status?.response === 'declined' ? 'declined'
                : a.status?.response === 'tentativelyAccepted' ? 'tentative'
                : 'needsAction',
        })),
    };
};

export const createEvent = async (
    accessToken: string,
    calendarId: string,
    event: any
) => {
    const requestBody = {
            subject: event.summary,
            body: {
                contentType: 'HTML',
                content: event.description || ''
            },
            start: ensureTimeZone(event.start),
            end: ensureTimeZone(event.end),
            isAllDay: !!event.allDay,
            location: {
                displayName: event.location || ''
            },
            ...(event.recurrence ? { recurrence: event.recurrence } : {}),
            ...(event.reminders?.length ? {
                isReminderOn: true,
                reminderMinutesBeforeStart: event.reminders[0].minutes,
            } : {}),
            ...(event.busyStatus ? { showAs: event.busyStatus === 'oof' ? 'oof' : event.busyStatus } : {}),
            ...(event.visibility ? {
                sensitivity: event.visibility === 'private' ? 'private'
                    : event.visibility === 'confidential' ? 'confidential'
                    : 'normal'
            } : {}),
            ...(event.attendees?.length ? {
                attendees: event.attendees.map((a: any) => ({
                    emailAddress: { address: a.email, name: a.displayName || a.email },
                    type: 'required',
                })),
            } : {}),
    };
    console.log('[OUTLOOK] createEvent request body:', JSON.stringify(requestBody, null, 2));

    const response = await fetch(`${GRAPH_ENDPOINT}/me/calendars/${encodeURIComponent(calendarId)}/events`, {
        method: 'POST',
        headers: {
            'Authorization': `Bearer ${accessToken}`,
            'Content-Type': 'application/json',
            'Prefer': 'outlook.timezone="UTC"'
        },
        body: JSON.stringify(requestBody)
    });

    if (!response.ok) {
        const err = await response.text();
        throw new Error(`Failed to create Outlook event: ${err}`);
    }

    const created = await response.json();
    return mapOutlookEventResponse(created);
};

/**
 * Update Outlook Event
 */
export const updateEvent = async (
    accessToken: string,
    calendarId: string, // Not strictly needed for Graph ID-based update but kept for interface consistency
    eventId: string,
    event: any
) => {
    const body = JSON.stringify({
        subject: event.summary,
        body: {
            contentType: 'HTML',
            content: event.description || ''
        },
        start: ensureTimeZone(event.start),
        end: ensureTimeZone(event.end),
        isAllDay: event.allDay !== undefined ? !!event.allDay : undefined,
        location: {
            displayName: event.location || ''
        },
        ...(event.recurrence ? { recurrence: event.recurrence } : {}),
        ...(event.reminders?.length ? {
            isReminderOn: true,
            reminderMinutesBeforeStart: event.reminders[0].minutes,
        } : {}),
        ...(event.busyStatus ? { showAs: event.busyStatus === 'oof' ? 'oof' : event.busyStatus } : {}),
        ...(event.visibility ? {
            sensitivity: event.visibility === 'private' ? 'private'
                : event.visibility === 'confidential' ? 'confidential'
                : 'normal'
        } : {}),
        ...(event.attendees?.length ? {
            attendees: event.attendees.map((a: any) => ({
                emailAddress: { address: a.email, name: a.displayName || a.email },
                type: 'required',
            })),
        } : {}),
    });

    console.log(`[OUTLOOK] updateEvent request body:`, body);

    const patchHeaders: Record<string, string> = {
        'Authorization': `Bearer ${accessToken}`,
        'Content-Type': 'application/json',
        'Prefer': 'outlook.timezone="UTC"'
    };

    const encodedEventId = encodeURIComponent(eventId);
    const encodedCalendarId = encodeURIComponent(calendarId);

    // Try non-calendar-scoped endpoint first (more reliable for all ID types)
    let response = await fetch(`${GRAPH_ENDPOINT}/me/events/${encodedEventId}`, {
        method: 'PATCH', headers: patchHeaders, body
    });
    console.log(`[OUTLOOK] PATCH /me/events/${eventId.slice(-20)} → ${response.status}`);

    if (!response.ok && (response.status === 404 || response.status === 400)) {
        // Fallback: try calendar-scoped endpoint
        response = await fetch(`${GRAPH_ENDPOINT}/me/calendars/${encodedCalendarId}/events/${encodedEventId}`, {
            method: 'PATCH', headers: patchHeaders, body
        });
        console.log(`[OUTLOOK] PATCH fallback /me/calendars/.../events/${eventId.slice(-20)} → ${response.status}`);
    }

    if (!response.ok) {
        const err = await response.text();
        console.error(`[OUTLOOK] Update failed:`, err);
        throw new Error(`Failed to update Outlook event: ${err}`);
    }

    const updated = await response.json();
    return mapOutlookEventResponse(updated);
};

/**
 * Delete Outlook Event
 */
export const deleteEvent = async (
    accessToken: string,
    calendarId: string,
    eventId: string
) => {
    const headers = { 'Authorization': `Bearer ${accessToken}` };
    // Encode the event ID for URL safety (base64 padding = may need encoding)
    const encodedEventId = encodeURIComponent(eventId);
    const encodedCalendarId = encodeURIComponent(calendarId);

    // Try non-calendar-scoped endpoint first (more reliable for all ID types)
    let response = await fetch(`${GRAPH_ENDPOINT}/me/events/${encodedEventId}`, {
        method: 'DELETE', headers
    });

    if (!response.ok && (response.status === 404 || response.status === 400)) {
        // Fallback: try calendar-scoped endpoint
        response = await fetch(`${GRAPH_ENDPOINT}/me/calendars/${encodedCalendarId}/events/${encodedEventId}`, {
            method: 'DELETE', headers
        });
    }

    // 404/410 on delete = already gone, treat as success
    if (response.status === 404 || response.status === 410) {
        console.log('[OUTLOOK] Event already deleted or not found, treating as success');
        return;
    }

    if (!response.ok) {
        const err = await response.text();
        throw new Error(`Failed to delete Outlook event: ${err}`);
    }
};
