import React, { createContext, useContext, useState, useCallback, useEffect, } from "react"; import { useUser, useAuth } from "@clerk/clerk-expo"; import { getNutritionByDate, saveDailyNutrition, addMealEntry, getMealEntriesByDate, type NutritionEntry, type MealEntry, } from "../api/nutrition"; import log from "../utils/logger"; interface NutritionContextValue { nutrition: NutritionEntry | null; meals: MealEntry[]; loading: boolean; error: Error | null; calorieGoal: number; todayCalories: number; percentage: number; addMeal: ( data: Omit, ) => Promise; updateGoal: (goal: number) => void; refetch: () => Promise; } const NutritionContext = createContext( undefined, ); export function NutritionProvider({ children }: { children: React.ReactNode }) { const { user } = useUser(); const { getToken } = useAuth(); const [nutrition, setNutrition] = useState(null); const [meals, setMeals] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [calorieGoal, setCalorieGoal] = useState(2000); // Default goal: 2000 cal const today = new Date().toISOString().split("T")[0]; const fetchTodayNutrition = useCallback(async () => { if (!user?.id) return; try { setLoading(true); setError(null); const token = await getToken(); // Fetch both nutrition data and meal entries const [nutritionData, mealsData] = await Promise.all([ getNutritionByDate(today, token), getMealEntriesByDate(today, token), ]); if (nutritionData) { setNutrition(nutritionData); if (nutritionData.calorieGoal) { setCalorieGoal(nutritionData.calorieGoal); } } else { setNutrition(null); } setMeals(mealsData); log.debug("Today's nutrition fetched", { nutritionData, mealsCount: mealsData.length, }); } catch (err) { const error = err instanceof Error ? err : new Error(String(err)); log.error("Failed to fetch nutrition", error); setError(error); } finally { setLoading(false); } }, [user?.id, getToken, today]); useEffect(() => { fetchTodayNutrition(); }, [fetchTodayNutrition]); const addMeal = useCallback( async (data: Omit) => { if (!user?.id) return; try { const token = await getToken(); // Add the meal entry const meal = await addMealEntry(data, token); // Recalculate total calories const currentTotal = nutrition?.totalCalories || 0; const newTotal = currentTotal + data.calories; // Update the daily nutrition const updatedNutrition = await saveDailyNutrition( { date: today, totalCalories: newTotal, calorieGoal, meals: [ ...(nutrition?.meals || []), { type: data.mealType, name: data.foodName, calories: data.calories, time: new Date().toISOString(), }, ], }, token, ); setNutrition(updatedNutrition); setMeals([...meals, meal]); log.debug("Meal added successfully", { meal, newTotal }); } catch (err) { const error = err instanceof Error ? err : new Error(String(err)); log.error("Failed to add meal", error); setError(error); throw error; } }, [user?.id, getToken, today, nutrition, calorieGoal, meals], ); const updateGoal = useCallback( async (goal: number) => { setCalorieGoal(goal); if (user?.id && nutrition) { try { const token = await getToken(); await saveDailyNutrition( { date: today, totalCalories: nutrition.totalCalories, calorieGoal: goal, meals: nutrition.meals, }, token, ); } catch (err) { log.error("Failed to update calorie goal", err); } } }, [user?.id, nutrition, today, getToken], ); const todayCalories = nutrition?.totalCalories || 0; const percentage = calorieGoal > 0 ? Math.round((todayCalories / calorieGoal) * 100) : 0; const value: NutritionContextValue = { nutrition, meals, loading, error, calorieGoal, todayCalories, percentage, addMeal, updateGoal, refetch: fetchTodayNutrition, }; return ( {children} ); } export function useNutrition() { const context = useContext(NutritionContext); if (context === undefined) { throw new Error("useNutrition must be used within a NutritionProvider"); } return context; }