import { useMemo, useState } from 'react'; import { Alert, Modal, Pressable, ScrollView, StyleSheet, Text, View } from 'react-native'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { router } from 'expo-router'; import { useTranslation } from 'react-i18next'; import { deleteMealPlanEntry, fetchMealPlanEntries, upsertMealPlanEntry } from '@/src/api/meal-plan.api'; import { fetchGoals } from '@/src/api/diet.api'; import { fetchRecipes } from '@/src/api/recipes.api'; import { Button } from '@/src/components/Button'; import { ErrorView } from '@/src/components/ErrorView'; import { Input } from '@/src/components/Input'; import { Screen } from '@/src/components/Screen'; import { useAppearance } from '@/src/context/AppearanceContext'; import { CONSUMPTION_CATEGORIES } from '@/src/types/diet'; import type { MealPlanEntry } from '@/src/types/meal-plan'; import { MealCategory } from '@/src/types/recipe'; import { addDays, formatDisplayDate, weekDays, weekRange } from '@/src/utils/date'; import { categoryLabel } from '@/src/utils/recipe'; import { computeDayMealPlanning, estimateEntryCalories, rankRecipeSuggestions, } from '@/src/utils/mealPlannerSuggestions'; const PLANNER_CATEGORIES = CONSUMPTION_CATEGORIES.filter((c) => c.value <= MealCategory.Dinner); export default function PlannerScreen() { const { t, i18n } = useTranslation(); const { colors } = useAppearance(); const queryClient = useQueryClient(); const [weekAnchor, setWeekAnchor] = useState(() => weekRange().from); const { from, to } = useMemo(() => weekRange(weekAnchor), [weekAnchor]); const days = useMemo(() => weekDays(from), [from]); const [picker, setPicker] = useState<{ date: string; mealCategory: number; entry?: MealPlanEntry } | null>(null); const [recipeSearch, setRecipeSearch] = useState(''); const [portions, setPortions] = useState('1'); const [dailyTargets, setDailyTargets] = useState>({}); const [selectedRecipeId, setSelectedRecipeId] = useState(null); const entriesQuery = useQuery({ queryKey: ['meal-plan', from, to], queryFn: () => fetchMealPlanEntries(from, to), }); const goalsQuery = useQuery({ queryKey: ['diet-goals'], queryFn: fetchGoals, }); const recipesQuery = useQuery({ queryKey: ['recipes', 'planner-search', recipeSearch], queryFn: () => fetchRecipes({ search: recipeSearch }), enabled: !!picker && recipeSearch.trim().length >= 2, }); const suggestionsQuery = useQuery({ queryKey: ['recipes', 'planner-suggestions', picker?.mealCategory], queryFn: () => fetchRecipes({ category: picker!.mealCategory as MealCategory }), enabled: !!picker, }); const entries = entriesQuery.data ?? []; const dailyTarget = picker ? (dailyTargets[picker.date] ?? goalsQuery.data?.calorieGoal ?? 0) : 0; const selectedRecipe = useMemo(() => { if (!selectedRecipeId) return null; return ( recipesQuery.data?.find((r) => r.id === selectedRecipeId) ?? suggestionsQuery.data?.find((r) => r.id === selectedRecipeId) ?? null ); }, [selectedRecipeId, recipesQuery.data, suggestionsQuery.data]); const estimatedMealCalories = estimateEntryCalories(selectedRecipe, Number(portions) || 1); const dayPlanning = useMemo(() => { if (!picker) return null; const pending = estimatedMealCalories != null && estimatedMealCalories > 0 ? { category: picker.mealCategory, calories: estimatedMealCalories } : null; return computeDayMealPlanning(picker.date, picker.mealCategory, entries, dailyTarget, pending); }, [picker, entries, dailyTarget, estimatedMealCalories]); const suggestions = useMemo(() => { if (!picker || !dayPlanning || !suggestionsQuery.data) return []; return rankRecipeSuggestions(suggestionsQuery.data, picker.mealCategory, dayPlanning.slotTarget); }, [picker, dayPlanning, suggestionsQuery.data]); const upsertMutation = useMutation({ mutationFn: upsertMealPlanEntry, onSuccess: () => { setPicker(null); setRecipeSearch(''); setPortions('1'); setSelectedRecipeId(null); void queryClient.invalidateQueries({ queryKey: ['meal-plan'] }); }, }); const deleteMutation = useMutation({ mutationFn: deleteMealPlanEntry, onSuccess: () => void queryClient.invalidateQueries({ queryKey: ['meal-plan'] }), }); function selectFromSearch(recipeId: number) { setSelectedRecipeId(recipeId); setRecipeSearch(''); } function selectFromSuggestion(recipeId: number) { setSelectedRecipeId(recipeId); } function clearSelection() { setSelectedRecipeId(null); } function entryFor(date: string, mealCategory: number): MealPlanEntry | undefined { return entriesQuery.data?.find((e) => e.planDate === date && e.mealCategory === mealCategory); } return ( setWeekAnchor(addDays(from, -7))} style={[styles.navBtn, { borderColor: colors.border, backgroundColor: colors.surface }]} > {formatDisplayDate(from, i18n.language)} – {formatDisplayDate(to, i18n.language)} setWeekAnchor(addDays(from, 7))} style={[styles.navBtn, { borderColor: colors.border, backgroundColor: colors.surface }]} >