from fastapi import APIRouter, Header, HTTPException, Depends from pydantic import BaseModel from sqlalchemy import select from app.config import AGENT_API_KEY from app.models.base import async_session from app.models.user import User from app.models.workout import Phase, Workout, WorkoutSet from app.models.checkin import Checkin router = APIRouter(prefix="/api/agent", tags=["agent"]) async def verify_agent(x_api_key: str = Header("")): if AGENT_API_KEY and x_api_key != AGENT_API_KEY: raise HTTPException(status_code=403, detail="invalid api key") class CreatePhaseRequest(BaseModel): name: str description: str = "" start_date: str | None = None end_date: str | None = None notes: str = "" class CreateWorkoutRequest(BaseModel): username: str name: str date: str phase_id: int | None = None notes: str = "" class CreateSetRequest(BaseModel): exercise: str set_number: int reps: int | None = None weight: float | None = None rpe: float | None = None notes: str = "" class CreateCheckinRequest(BaseModel): username: str date: str feeling: str | None = None weight_lb: float | None = None calories: int | None = None steps: int | None = None sleep_hours: float | None = None notes: str = "" @router.post("/workouts") async def agent_create_workout( body: CreateWorkoutRequest, _=Depends(verify_agent), ): async with async_session() as session: result = await session.execute( select(User).where(User.username == body.username) ) user = result.scalar_one_or_none() if not user: raise HTTPException(status_code=404, detail="user not found") workout = Workout( user_id=user.id, phase_id=body.phase_id, name=body.name, date=body.date, notes=body.notes, status="plan", ) session.add(workout) await session.commit() await session.refresh(workout) return {"id": workout.id, "name": workout.name, "date": workout.date, "status": workout.status} @router.post("/workouts/{workout_id}/sets") async def agent_add_set( workout_id: int, body: CreateSetRequest, _=Depends(verify_agent), ): async with async_session() as session: ws = WorkoutSet( workout_id=workout_id, exercise=body.exercise, set_number=body.set_number, reps=body.reps, weight=body.weight, rpe=body.rpe, notes=body.notes, ) session.add(ws) await session.commit() await session.refresh(ws) return {"id": ws.id, "exercise": ws.exercise, "set_number": ws.set_number} @router.put("/workouts/{workout_id}/complete") async def agent_complete_workout( workout_id: int, _=Depends(verify_agent), ): async with async_session() as session: result = await session.execute( select(Workout).where(Workout.id == workout_id) ) workout = result.scalar_one_or_none() if not workout: raise HTTPException(status_code=404, detail="workout not found") workout.status = "complete" await session.commit() return {"id": workout.id, "status": "complete"} @router.post("/checkins") async def agent_create_checkin( body: CreateCheckinRequest, _=Depends(verify_agent), ): async with async_session() as session: result = await session.execute( select(User).where(User.username == body.username) ) user = result.scalar_one_or_none() if not user: raise HTTPException(status_code=404, detail="user not found") checkin = Checkin( user_id=user.id, date=body.date, feeling=body.feeling, weight_lb=body.weight_lb, calories=body.calories, steps=body.steps, sleep_hours=body.sleep_hours, notes=body.notes, ) session.add(checkin) await session.commit() await session.refresh(checkin) return {"id": checkin.id, "date": checkin.date} @router.get("/phases") async def agent_list_phases(_=Depends(verify_agent)): async with async_session() as session: result = await session.execute(select(Phase).order_by(Phase.start_date.nulls_last())) phases = result.scalars().all() return [ {"id": p.id, "name": p.name, "description": p.description, "start_date": p.start_date, "end_date": p.end_date, "notes": p.notes} for p in phases ] @router.post("/phases") async def agent_create_phase( body: CreatePhaseRequest, _=Depends(verify_agent), ): async with async_session() as session: phase = Phase( name=body.name, description=body.description, start_date=body.start_date, end_date=body.end_date, notes=body.notes, ) session.add(phase) await session.commit() await session.refresh(phase) return {"id": phase.id, "name": phase.name} @router.put("/phases/{phase_id}") async def agent_update_phase( phase_id: int, body: CreatePhaseRequest, _=Depends(verify_agent), ): async with async_session() as session: result = await session.execute(select(Phase).where(Phase.id == phase_id)) phase = result.scalar_one_or_none() if not phase: raise HTTPException(status_code=404, detail="phase not found") phase.name = body.name phase.description = body.description phase.start_date = body.start_date phase.end_date = body.end_date phase.notes = body.notes await session.commit() return {"id": phase.id, "name": phase.name}