import React, { createContext, useContext, useState, useCallback, useEffect, } from "react"; import { useUser, useAuth } from "@clerk/clerk-expo"; import { getHydrationByDate, saveDailyHydration, type HydrationEntry, } from "../api/hydration"; import log from "../utils/logger"; interface HydrationContextValue { hydration: HydrationEntry | null; loading: boolean; error: Error | null; waterGoal: number; todayTotal: number; percentage: number; addWater: (amount: number) => Promise; resetToday: () => Promise; setWaterGoal: (goal: number) => void; refetch: () => Promise; } const HydrationContext = createContext( undefined, ); export function HydrationProvider({ children }: { children: React.ReactNode }) { const { user } = useUser(); const { getToken } = useAuth(); const [hydration, setHydration] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [waterGoal, setWaterGoal] = useState(2000); // Default goal: 2000ml const today = new Date().toISOString().split("T")[0]; const fetchTodayHydration = useCallback(async () => { if (!user?.id) return; try { setLoading(true); setError(null); const token = await getToken(); const data = await getHydrationByDate(today, token); if (data) { setHydration(data); if (data.waterGoal) { setWaterGoal(data.waterGoal); } } else { // No data for today yet setHydration(null); } log.debug("Today's hydration fetched", { data }); } catch (err) { const error = err instanceof Error ? err : new Error(String(err)); log.error("Failed to fetch hydration", error); setError(error); } finally { setLoading(false); } }, [user?.id, getToken, today]); useEffect(() => { fetchTodayHydration(); }, [fetchTodayHydration]); useEffect(() => { setHydration(null); setError(null); setLoading(false); setWaterGoal(2000); if (user?.id) { log.debug("Hydration state reset for user", { userId: user.id }); } else { log.debug("Hydration state reset on sign-out"); } }, [user?.id]); const addWater = useCallback( async (amount: number) => { if (!user?.id) return; try { const token = await getToken(); const currentTotal = hydration?.totalWater || 0; const newTotal = currentTotal + amount; const entry = await saveDailyHydration( { date: today, totalWater: newTotal, waterGoal, entries: [ ...(hydration?.entries || []), { amount, time: new Date().toISOString() }, ], }, token, ); setHydration(entry); log.debug("Water added successfully", { amount, newTotal }); } catch (err) { const error = err instanceof Error ? err : new Error(String(err)); log.error("Failed to add water", error); setError(error); throw error; // Re-throw so UI can handle it } }, [user?.id, getToken, today, hydration, waterGoal], ); const resetToday = useCallback(async () => { if (!user?.id) return; try { const token = await getToken(); await saveDailyHydration( { date: today, totalWater: 0, waterGoal, entries: [], }, token, ); setHydration({ date: today, totalWater: 0, waterGoal, entries: [], }); log.debug("Hydration reset for today"); } catch (err) { const error = err instanceof Error ? err : new Error(String(err)); log.error("Failed to reset hydration", error); setError(error); throw error; } }, [user?.id, getToken, today, waterGoal]); const todayTotal = hydration?.totalWater || 0; const percentage = waterGoal > 0 ? Math.round((todayTotal / waterGoal) * 100) : 0; const value: HydrationContextValue = { hydration, loading, error, waterGoal, todayTotal, percentage, addWater, resetToday, setWaterGoal, refetch: fetchTodayHydration, }; return ( {children} ); } export function useHydration() { const context = useContext(HydrationContext); if (context === undefined) { throw new Error("useHydration must be used within a HydrationProvider"); } return context; }