from __future__ import annotations from fastapi import APIRouter, Depends, HTTPException from sqlmodel import Session from tracker.config import load_config from tracker.db import get_session from tracker.food_service import ( append_meal_log, delete_planned_meal, list_meal_logs, list_planned_meals, upsert_planned_meal, ) from tracker.models import MealLogCreateInput, MealPlanUpsertInput, MealLogRow, MealPlanRow from tracker.planner_schemas import ( DailyWeighinResponse, DeleteEventResponse, RecipeSearchResponse, ScheduleEventInput, ScheduledEvent, ScheduleWorkoutsInput, TrackerStateResponse, ) from tracker.planner_service import ( build_shopping_gap, build_tracker_state, create_daily_weighin, delete_calendar_event, fetch_calendar_view, fetch_pantry, run_schedule_event, run_schedule_workouts, search_recipes, ) from tracker.agent_profile import resolve_agent_profile router = APIRouter(prefix="/api/planner") @router.get("/calendar") def view_calendar(days_ahead: int = 7): return fetch_calendar_view(days_ahead) @router.post("/schedule-workouts") def api_schedule_workouts( body: ScheduleWorkoutsInput, session: Session = Depends(get_session), ) -> list[ScheduledEvent]: cfg = load_config() data, err = run_schedule_workouts(session, cfg, body) if err is not None: raise HTTPException(status_code=404, detail=err) if data is None: raise HTTPException(status_code=404, detail="No program found") return data @router.post("/schedule-event") def api_schedule_event(body: ScheduleEventInput) -> ScheduledEvent: cfg = load_config() return run_schedule_event(cfg, body) @router.delete("/calendar/{event_id}") def api_delete_event(event_id: str) -> DeleteEventResponse: cfg = load_config() return delete_calendar_event(cfg, event_id) @router.post("/daily-weighin") def api_daily_weighin() -> DailyWeighinResponse: cfg = load_config() return create_daily_weighin(cfg) @router.get("/pantry") def view_pantry(user_id: int = 5): cfg = load_config() return fetch_pantry(cfg, user_id) @router.get("/recipes/search") def search_recipes_route( q: str, max_active_minutes: int | None = None, max_total_minutes: int | None = None, min_calories: int | None = None, max_calories: int | None = None, min_protein: float | None = None, limit: int = 20, ) -> RecipeSearchResponse: cfg = load_config() profile = resolve_agent_profile(cfg) defaults = profile.recipe_search_defaults eff_max_active = max_active_minutes if max_active_minutes is not None else defaults.max_active_minutes eff_max_total = max_total_minutes if max_total_minutes is not None else defaults.max_total_minutes eff_min_cal = min_calories if min_calories is not None else defaults.min_calories eff_max_cal = max_calories if max_calories is not None else defaults.max_calories eff_min_prot = min_protein if min_protein is not None else defaults.min_protein eff_limit = defaults.search_limit if defaults.search_limit is not None else limit return search_recipes( cfg, q, eff_max_active, eff_max_total, eff_min_cal, eff_max_cal, eff_min_prot, eff_limit, ) @router.get("/tracker-state") def view_tracker_state(session: Session = Depends(get_session)) -> TrackerStateResponse: cfg = load_config() return build_tracker_state(session, cfg) @router.get("/shopping-list") def build_shopping_list( ingredients: str, user_id: int = 5, ): cfg = load_config() return build_shopping_gap(cfg, ingredients, user_id) @router.get("/meal-plan") def get_meal_plan_route( start_date: str, end_date: str, session: Session = Depends(get_session), ) -> list[MealPlanRow]: return list_planned_meals(session, start_date, end_date) @router.post("/meal-plan") def post_meal_plan_route( body: MealPlanUpsertInput, session: Session = Depends(get_session), ) -> MealPlanRow: return upsert_planned_meal(session, body) @router.delete("/meal-plan/{planned_meal_id}") def delete_meal_plan_route( planned_meal_id: int, session: Session = Depends(get_session), ) -> dict[str, str]: ok = delete_planned_meal(session, planned_meal_id) if not ok: raise HTTPException(status_code=404, detail="Planned meal not found") return {"deleted": str(planned_meal_id)} @router.get("/meal-logs") def get_meal_logs_route( start_date: str, end_date: str, session: Session = Depends(get_session), ) -> list[MealLogRow]: return list_meal_logs(session, start_date, end_date) @router.post("/meal-logs") def post_meal_log_route( body: MealLogCreateInput, session: Session = Depends(get_session), ) -> MealLogRow: return append_meal_log(session, body)