"""Foods router (spec §3.1). Thin: validate → service → schema (spec §8.1 rule 2).""" from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session from database import get_db from schemas import FoodCreate, FoodRead, FoodUpdate from services.foods import ( BarcodeConflictError, create_food, delete_food, get_food, get_recent_foods, list_foods, update_food, ) router = APIRouter(prefix="/api/foods", tags=["foods"]) @router.get("/recent", response_model=list[FoodRead]) def _recent_foods( limit: int = Query(default=10, le=50), db: Session = Depends(get_db), ): """Foods ordered by most recent appearance in daily_log, deduplicated. Soft-deleted foods excluded. Ordered by most-recently-LOGGED, not by foods.created_at.""" return get_recent_foods(db, limit=limit) @router.get("", response_model=list[FoodRead]) def _list_foods( q: str | None = None, barcode: str | None = None, limit: int = Query(default=50, le=200), offset: int = 0, include_deleted: bool = False, db: Session = Depends(get_db), ): """Search foods. q matches name AND brand. Soft-deleted excluded by default.""" return list_foods(db, q=q, barcode=barcode, limit=limit, offset=offset, include_deleted=include_deleted) @router.get("/{food_id}", response_model=FoodRead) def _get_food(food_id: int, db: Session = Depends(get_db)): """Get a single food, including soft-deleted ones (for historical logs).""" food = get_food(db, food_id) if food is None: raise HTTPException(status_code=404, detail="Food not found") return food @router.post("", response_model=FoodRead, status_code=201) def _create_food(data: FoodCreate, db: Session = Depends(get_db)): """Create a food. Barcode uniqueness enforced on live foods → 409.""" try: return create_food(db, data) except BarcodeConflictError as e: raise HTTPException(status_code=409, detail=str(e)) @router.put("/{food_id}", response_model=FoodRead) def _update_food(food_id: int, data: FoodUpdate, db: Session = Depends(get_db)): """Update editable fields. Only supplied fields are changed.""" try: result = update_food(db, food_id, data) except BarcodeConflictError as e: raise HTTPException(status_code=409, detail=str(e)) if result is None: raise HTTPException(status_code=404, detail="Food not found") return result @router.delete("/{food_id}", response_model=FoodRead) def _delete_food(food_id: int, db: Session = Depends(get_db)): """Soft-delete: sets deleted_at. Food hidden from search, still visible by id.""" result = delete_food(db, food_id) if result is None: raise HTTPException(status_code=404, detail="Food not found") return result