from fastapi import APIRouter, Depends, HTTPException, Response from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import joinedload, selectinload from app.core.database import get_session from app.models.encounter import Encounter from app.models.game import Game from app.models.nuzlocke_run import NuzlockeRun from app.schemas.run import RunCreate, RunDetailResponse, RunResponse, RunUpdate router = APIRouter() @router.post("", response_model=RunResponse, status_code=201) async def create_run( data: RunCreate, session: AsyncSession = Depends(get_session) ): # Validate game exists game = await session.get(Game, data.game_id) if game is None: raise HTTPException(status_code=404, detail="Game not found") run = NuzlockeRun( game_id=data.game_id, name=data.name, status="active", rules=data.rules, ) session.add(run) await session.commit() await session.refresh(run) return run @router.get("", response_model=list[RunResponse]) async def list_runs(session: AsyncSession = Depends(get_session)): result = await session.execute( select(NuzlockeRun).order_by(NuzlockeRun.started_at.desc()) ) return result.scalars().all() @router.get("/{run_id}", response_model=RunDetailResponse) async def get_run(run_id: int, session: AsyncSession = Depends(get_session)): result = await session.execute( select(NuzlockeRun) .where(NuzlockeRun.id == run_id) .options( joinedload(NuzlockeRun.game), selectinload(NuzlockeRun.encounters) .joinedload(Encounter.pokemon), selectinload(NuzlockeRun.encounters) .joinedload(Encounter.current_pokemon), selectinload(NuzlockeRun.encounters) .joinedload(Encounter.route), ) ) run = result.scalar_one_or_none() if run is None: raise HTTPException(status_code=404, detail="Run not found") return run @router.patch("/{run_id}", response_model=RunResponse) async def update_run( run_id: int, data: RunUpdate, session: AsyncSession = Depends(get_session), ): run = await session.get(NuzlockeRun, run_id) if run is None: raise HTTPException(status_code=404, detail="Run not found") update_data = data.model_dump(exclude_unset=True) for field, value in update_data.items(): setattr(run, field, value) await session.commit() await session.refresh(run) return run @router.delete("/{run_id}", status_code=204) async def delete_run( run_id: int, session: AsyncSession = Depends(get_session) ): run = await session.get(NuzlockeRun, run_id) if run is None: raise HTTPException(status_code=404, detail="Run not found") # Delete associated encounters first encounters = await session.execute( select(Encounter).where(Encounter.run_id == run_id) ) for enc in encounters.scalars(): await session.delete(enc) await session.delete(run) await session.commit() return Response(status_code=204)