import { useState, useEffect, useCallback } from "react"; import { useAuthContext } from "@/contexts/AuthContext"; import { useAtomValue } from "jotai"; import { useFetchAndSaveGoogleEvents } from "./useFetchAndSaveGoogleEvents"; import { useFetchAndSaveAppleEvents } from "./useFetchAndSaveAppleEvents"; import { useFetchAndSaveMicrosoftEvents } from "./useFetchAndSaveOutlookEvents"; import { selectedDateAtom } from "@/components/pages/calendar/atoms"; import { addDays, subDays, isBefore, isAfter, format } from "date-fns"; interface SyncResponse { success: boolean; eventCount?: number; error?: string; } export const useSyncEvents = () => { const { profileData } = useAuthContext(); const selectedDate = useAtomValue(selectedDateAtom); const [lastSyncDate, setLastSyncDate] = useState(selectedDate); const [lowerBoundDate, setLowerBoundDate] = useState(subDays(selectedDate, 6 * 30)); const [upperBoundDate, setUpperBoundDate] = useState(addDays(selectedDate, 6 * 30)); const [isSyncing, setIsSyncing] = useState(false); const [error, setError] = useState(null); const [syncStats, setSyncStats] = useState<{ total: number; success: number; failed: number; events: number; }>({ total: 0, success: 0, failed: 0, events: 0 }); const syncedRanges = useState>(new Set())[0]; const { mutateAsync: fetchAndSaveGoogleEvents } = useFetchAndSaveGoogleEvents(); const { mutateAsync: fetchAndSaveOutlookEvents } = useFetchAndSaveMicrosoftEvents(); const { mutateAsync: fetchAndSaveAppleEvents } = useFetchAndSaveAppleEvents(); const generateRangeKey = (startDate: Date, endDate: Date) => { return `${format(startDate, "yyyy-MM-dd")}_${format(endDate, "yyyy-MM-dd")}`; }; const syncEvents = useCallback(async () => { setIsSyncing(true); setError(null); const newLowerBound = subDays(selectedDate, 6 * 30); const newUpperBound = addDays(selectedDate, 6 * 30); const rangeKey = generateRangeKey(newLowerBound, newUpperBound); if (syncedRanges.has(rangeKey)) { setIsSyncing(false); return; } if (isBefore(selectedDate, lowerBoundDate) || isAfter(selectedDate, upperBoundDate)) { const results: SyncResponse[] = []; const stats = { total: 0, success: 0, failed: 0, events: 0 }; try { if (profileData?.googleAccounts) { for (const [email] of Object.entries(profileData.googleAccounts)) { try { stats.total++; const result = await fetchAndSaveGoogleEvents({ email }) as SyncResponse; if (result.success) { stats.success++; stats.events += result.eventCount || 0; } else { stats.failed++; } results.push(result); } catch (err) { stats.failed++; console.error(`Failed to sync Google calendar for ${email}:`, err); } } } if (profileData?.microsoftAccounts) { for (const [email] of Object.entries(profileData.microsoftAccounts)) { try { stats.total++; const result = await fetchAndSaveOutlookEvents({ email }); if (result.success) { stats.success++; stats.events += result.eventCount || 0; } else { stats.failed++; } results.push(result); } catch (err) { stats.failed++; console.error(`Failed to sync Microsoft calendar for ${email}:`, err); } } } if (profileData?.appleAccounts) { for (const [email] of Object.entries(profileData.appleAccounts)) { try { stats.total++; const result = await fetchAndSaveAppleEvents({ email }); } catch (err) { stats.failed++; console.error(`Failed to sync Apple calendar for ${email}:`, err); } } } setSyncStats(stats); setLastSyncDate(selectedDate); setLowerBoundDate(newLowerBound); setUpperBoundDate(newUpperBound); syncedRanges.add(rangeKey); if (stats.failed > 0) { throw new Error(`Failed to sync ${stats.failed} calendars`); } } catch (err: any) { console.error("Error syncing events:", err); setError(err); } finally { setIsSyncing(false); } } else { setIsSyncing(false); } }, [ selectedDate, lowerBoundDate, upperBoundDate, profileData, fetchAndSaveGoogleEvents, fetchAndSaveOutlookEvents, fetchAndSaveAppleEvents, syncedRanges ]); useEffect(() => { syncEvents(); }, [selectedDate, syncEvents]); return { isSyncing, error, lastSyncDate, lowerBoundDate, upperBoundDate, syncStats, }; };