import uuid from fastapi import APIRouter, Depends, HTTPException from sqlalchemy import select, update from sqlalchemy.ext.asyncio import AsyncSession from app.auth.users import current_active_user from app.db import get_async_session from app.models.character import Character from app.models.game import GameParticipant from app.models.message import Message from app.models.user import User from app.schemas.character import CharacterRead router = APIRouter(prefix="/api/characters", tags=["characters"]) @router.get("", response_model=list[CharacterRead]) async def list_my_characters( session: AsyncSession = Depends(get_async_session), user: User = Depends(current_active_user), ) -> list[Character]: characters = ( await session.execute( select(Character) .where(Character.owner_id == user.id) .order_by(Character.created_at.desc()) ) ).scalars().all() return list(characters) @router.get("/{character_id}", response_model=CharacterRead) async def get_character( character_id: uuid.UUID, session: AsyncSession = Depends(get_async_session), user: User = Depends(current_active_user), ) -> Character: character = await session.get(Character, character_id) if character is None: raise HTTPException(status_code=404, detail="Character not found") if character.owner_id != user.id: raise HTTPException(status_code=403, detail="Not your character") return character @router.delete("/{character_id}", status_code=204) async def delete_character( character_id: uuid.UUID, session: AsyncSession = Depends(get_async_session), user: User = Depends(current_active_user), ) -> None: character = await session.get(Character, character_id) if character is None: raise HTTPException(status_code=404, detail="Character not found") if character.owner_id != user.id: raise HTTPException(status_code=403, detail="Not your character") # game_participants.character_id and messages.character_id have no ON DELETE cascade # (a character can outlive its games and vice versa) — detach them first so the delete # doesn't hit a FK violation. Games and message history stay intact either way. await session.execute( update(GameParticipant) .where(GameParticipant.character_id == character_id) .values(character_id=None) ) await session.execute( update(Message).where(Message.character_id == character_id).values(character_id=None) ) await session.delete(character) await session.commit()