"""Profile management routes. Users can view, create, edit profiles and switch the active profile. """ import structlog from fastapi import APIRouter, Depends, Request from fastapi.responses import HTMLResponse, RedirectResponse from sqlmodel import Session from app.database import get_db_session from app.models.user import User from app.services.user_service import UserService from app.utils.auth import require_active_profile, get_active_profile_id logger = structlog.get_logger(__name__) router = APIRouter(prefix="/profiles", tags=["profiles"]) @router.get("", response_class=HTMLResponse) async def list_profiles( request: Request, session: Session = Depends(get_db_session), profile: User = Depends(require_active_profile), ): """List all user profiles.""" user_service = UserService(session) profiles = user_service.list_users() active_profile_id = get_active_profile_id(request) templates = request.app.state.templates return templates.TemplateResponse("pages/profiles.html", { "request": request, "profiles": profiles, "active_profile_id": active_profile_id, }) @router.post("/switch") async def switch_profile( request: Request, session: Session = Depends(get_db_session), ): """Switch the active user profile. Sets a cookie with the selected profile ID (1 year expiry). """ form = await request.form() profile_id = form.get("profile_id", "") user_service = UserService(session) profile = user_service.get_user_by_id(int(profile_id)) if profile_id.isdigit() else None response = RedirectResponse(url="/workouts", status_code=303) if profile: response.set_cookie( key="active_profile_id", value=str(profile.id), httponly=True, samesite="lax", max_age=31536000, # 1 year ) logger.info("profile_switched", profile_id=profile.id, name=profile.display_name) else: logger.warning("profile_switch_failed", profile_id=profile_id) response = RedirectResponse(url="/", status_code=303) return response @router.get("/create", response_class=HTMLResponse) async def create_profile_form(request: Request): """Render the create-profile form.""" templates = request.app.state.templates return templates.TemplateResponse("pages/profile_create.html", { "request": request, }) @router.post("/create") async def create_profile( request: Request, session: Session = Depends(get_db_session), ): """Create a new profile, set cookie, redirect to workouts.""" form = await request.form() display_name = form.get("display_name", "").strip() if not display_name: templates = request.app.state.templates return templates.TemplateResponse("pages/profile_create.html", { "request": request, "error": "Display name is required.", }) user_service = UserService(session) # Generate username from display name username = display_name.lower().replace(" ", "_") # Ensure uniqueness existing = user_service.get_user_by_username(username) if existing: templates = request.app.state.templates return templates.TemplateResponse("pages/profile_create.html", { "request": request, "error": "A profile with that name already exists.", }) profile = user_service.create_user( username=username, display_name=display_name, height=form.get("height", "").strip() or None, weight=form.get("weight", "").strip() or None, goals=form.get("goals", "").strip() or None, ) response = RedirectResponse(url="/workouts", status_code=303) response.set_cookie( key="active_profile_id", value=str(profile.id), httponly=True, samesite="lax", max_age=31536000, # 1 year ) logger.info("profile_created", profile_id=profile.id, name=profile.display_name) return response @router.get("/{profile_id}/edit", response_class=HTMLResponse) async def edit_profile_page( profile_id: int, request: Request, session: Session = Depends(get_db_session), active_profile: User = Depends(require_active_profile), ): """Render the profile edit form.""" user_service = UserService(session) profile = user_service.get_user_by_id(profile_id) templates = request.app.state.templates return templates.TemplateResponse("pages/profile_edit.html", { "request": request, "profile": profile, }) @router.post("/{profile_id}/edit") async def update_profile( profile_id: int, request: Request, session: Session = Depends(get_db_session), active_profile: User = Depends(require_active_profile), ): """Process profile edit form submission.""" form = await request.form() user_service = UserService(session) user_service.update_user( profile_id, display_name=form.get("display_name", ""), height=form.get("height", ""), weight=form.get("weight", ""), goals=form.get("goals", ""), ) return RedirectResponse(url="/profiles", status_code=303)