"""Deck management router endpoints.""" from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, delete, update, func from typing import Optional, List from app.core.database import get_db from app.core.security import get_current_user from app.models.models import DecklistFile, DecklistFolder from app.models.mirror_models import DeckCardLink from app.schemas.schemas import DeckCreate, DeckUpdate, DeckResponse, DeckWithCardsResponse, FolderCreate, FolderResponse router = APIRouter() @router.get("/", response_model=List[DeckWithCardsResponse]) async def list_decks( folder_id: Optional[int] = None, page: int = 1, page_size: int = 50, db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), ): """List decks for current user.""" user_id = int(current_user["user_id"]) if folder_id: stmt = ( select(DecklistFile) .where( DecklistFile.owner_id == user_id, DecklistFile.folder_id == folder_id, ) .offset((page - 1) * page_size) .limit(page_size) ) else: stmt = ( select(DecklistFile) .where(DecklistFile.owner_id == user_id) .offset((page - 1) * page_size) .limit(page_size) ) result = await db.execute(stmt) decks = result.scalars().all() # Include card counts for each deck deck_responses = [] for deck in decks: # Get card count count_stmt = select(func.count()).select_from(DeckCardLink).where( DeckCardLink.deck_id == deck.id ) count_result = await db.execute(count_stmt) card_count = count_result.scalar() or 0 deck_data = DeckWithCardsResponse.model_validate(deck) deck_data.card_count = card_count deck_responses.append(deck_data) return deck_responses @router.post("/", response_model=DeckWithCardsResponse) async def create_deck( request: DeckCreate, db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), ): """Create a new deck.""" user_id = int(current_user["user_id"]) # Verify folder exists if specified if request.folder_id: stmt = select(DecklistFolder).where( DecklistFolder.id == request.folder_id, DecklistFolder.owner_id == user_id, ) result = await db.execute(stmt) folder = result.scalar_one_or_none() if not folder: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Folder not found", ) new_deck = DecklistFile( owner_id=user_id, folder_id=request.folder_id, name=request.name, content=request.content, format=request.format, status=request.status, ) db.add(new_deck) await db.flush() # Return with card count (0 for new deck) result = DeckWithCardsResponse.model_validate(new_deck) result.card_count = 0 return result @router.get("/folders", response_model=List[FolderResponse]) async def list_folders( parent_id: Optional[int] = None, db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), ): """List folders for current user.""" user_id = int(current_user["user_id"]) if parent_id: stmt = select(DecklistFolder).where( DecklistFolder.parent_id == parent_id, DecklistFolder.owner_id == user_id, ) else: stmt = select(DecklistFolder).where( DecklistFolder.parent_id == None, # Top-level folders DecklistFolder.owner_id == user_id, ) result = await db.execute(stmt) folders = result.scalars().all() return [FolderResponse.model_validate(folder) for folder in folders] @router.post("/folders", response_model=FolderResponse) async def create_folder( request: FolderCreate, db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), ): """Create a new folder.""" user_id = int(current_user["user_id"]) # Verify parent folder exists if specified if request.parent_id: stmt = select(DecklistFolder).where( DecklistFolder.id == request.parent_id, DecklistFolder.owner_id == user_id, ) result = await db.execute(stmt) parent_folder = result.scalar_one_or_none() if not parent_folder: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Parent folder not found", ) new_folder = DecklistFolder( owner_id=user_id, name=request.name, parent_id=request.parent_id, ) db.add(new_folder) await db.flush() return FolderResponse.model_validate(new_folder) @router.delete("/folders/{folder_id}") async def delete_folder( folder_id: int, db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), ): """Delete folder and all its contents.""" user_id = int(current_user["user_id"]) stmt = select(DecklistFolder).where( DecklistFolder.id == folder_id, DecklistFolder.owner_id == user_id, ) result = await db.execute(stmt) folder = result.scalar_one_or_none() if not folder: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Folder not found", ) # Delete folder and all decks (cascading delete) await db.execute(delete(DecklistFolder).where(DecklistFolder.id == folder_id)) return {"message": "Folder deleted successfully"} @router.get("/{deck_id}", response_model=DeckWithCardsResponse) async def get_deck( deck_id: int, db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), ): """Get deck by ID.""" user_id = int(current_user["user_id"]) stmt = select(DecklistFile).where( DecklistFile.id == deck_id, DecklistFile.owner_id == user_id, ) result = await db.execute(stmt) deck = result.scalar_one_or_none() if not deck: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Deck not found", ) # Get card count count_stmt = select(func.count()).select_from(DeckCardLink).where( DeckCardLink.deck_id == deck_id ) count_result = await db.execute(count_stmt) card_count = count_result.scalar() or 0 result = DeckWithCardsResponse.model_validate(deck) result.card_count = card_count return result @router.patch("/{deck_id}", response_model=DeckWithCardsResponse) async def update_deck( deck_id: int, request: DeckUpdate, db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), ): """Update deck.""" user_id = int(current_user["user_id"]) stmt = select(DecklistFile).where( DecklistFile.id == deck_id, DecklistFile.owner_id == user_id, ) result = await db.execute(stmt) deck = result.scalar_one_or_none() if not deck: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Deck not found", ) # Update fields update_data = request.model_dump(exclude_unset=True) stmt = ( update(DecklistFile) .where(DecklistFile.id == deck_id) .values(**update_data) ) await db.execute(stmt) await db.flush() # Fetch updated deck stmt = select(DecklistFile).where(DecklistFile.id == deck_id) result = await db.execute(stmt) updated_deck = result.scalar_one_or_none() # Get card count count_stmt = select(func.count()).select_from(DeckCardLink).where( DeckCardLink.deck_id == deck_id ) count_result = await db.execute(count_stmt) card_count = count_result.scalar() or 0 result = DeckWithCardsResponse.model_validate(updated_deck) result.card_count = card_count return result @router.delete("/{deck_id}") async def delete_deck( deck_id: int, db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), ): """Delete deck.""" user_id = int(current_user["user_id"]) stmt = select(DecklistFile).where( DecklistFile.id == deck_id, DecklistFile.owner_id == user_id, ) result = await db.execute(stmt) deck = result.scalar_one_or_none() if not deck: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Deck not found", ) await db.execute(delete(DecklistFile).where(DecklistFile.id == deck_id)) return {"message": "Deck deleted successfully"}