import { useMutation, useQueryClient } from "react-query"; import { useAuthContext } from "@/contexts/AuthContext"; import { useSetUserData } from "@/hooks/firebase/useSetUserData"; import functions from '@react-native-firebase/functions'; import * as AuthSession from 'expo-auth-session'; interface SyncResponse { success: boolean; eventCount: number; message?: string; } interface SyncError extends Error { code?: string; details?: { requiresReauth?: boolean; message?: string; }; } const microsoftConfig = { clientId: "13c79071-1066-40a9-9f71-b8c4b138b4af", scopes: [ "openid", "profile", "email", "offline_access", "Calendars.ReadWrite", "User.Read", ], redirectUri: AuthSession.makeRedirectUri({path: "settings"}) }; export const useFetchAndSaveMicrosoftEvents = () => { const queryClient = useQueryClient(); const { profileData } = useAuthContext(); const { mutateAsync: setUserData } = useSetUserData(); const handleReauth = async (email: string) => { try { const authRequest = new AuthSession.AuthRequest({ clientId: microsoftConfig.clientId, scopes: microsoftConfig.scopes, redirectUri: microsoftConfig.redirectUri, responseType: AuthSession.ResponseType.Code, usePKCE: true, }); const result = await authRequest.promptAsync({ authorizationEndpoint: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize', }); if (result.type === 'success' && result.params?.code) { const tokenResponse = await fetch('https://login.microsoftonline.com/common/oauth2/v2.0/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ client_id: microsoftConfig.clientId, scope: microsoftConfig.scopes.join(' '), code: result.params.code, redirect_uri: microsoftConfig.redirectUri, grant_type: 'authorization_code', code_verifier: authRequest.codeVerifier || '', }), }); const tokens = await tokenResponse.json(); await setUserData({ newUserData: { microsoftAccounts: { ...profileData?.microsoftAccounts, [email]: { accessToken: tokens.access_token, refreshToken: tokens.refresh_token, email, } } } }); return true; } return false; } catch (error) { console.error('Microsoft reauth error:', error); return false; } }; return useMutation({ mutationKey: ["fetchAndSaveOutlookEvents", "sync"], mutationFn: async ({ email }: { email?: string }) => { if (!email) { throw new Error("Email is required"); } if (!profileData?.microsoftAccounts?.[email]) { throw new Error("No valid Microsoft account found"); } try { const response = await functions() .httpsCallable('triggerMicrosoftSync')({ email }); return response.data as SyncResponse; } catch (error: any) { console.error("Microsoft sync error:", error); // Check if we need to reauthenticate if (error.details?.requiresReauth || error.code === 'functions/failed-precondition' || error.code === 'functions/unauthenticated') { console.log('Attempting Microsoft reauth...'); const reauthSuccessful = await handleReauth(email); if (reauthSuccessful) { // Retry the sync with new tokens console.log('Retrying sync after reauth...'); const retryResponse = await functions() .httpsCallable('triggerMicrosoftSync')({ email }); return retryResponse.data as SyncResponse; } } throw error; } }, onSuccess: (data) => { queryClient.invalidateQueries(["events"]); console.log(`Successfully synced ${data.eventCount} Microsoft events`); }, onError: (error) => { console.error('Microsoft sync failed:', { message: error.message, code: error.code, details: error.details }); } }); };