"""Deck management router endpoints.""" from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, delete, update 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.schemas.schemas import DeckCreate, DeckUpdate, DeckResponse, FolderCreate, FolderResponse router = APIRouter() @router.get("/", response_model=List[DeckResponse]) 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() return [DeckResponse.model_validate(deck) for deck in decks] @router.post("/", response_model=DeckResponse) 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, ) db.add(new_deck) await db.flush() return DeckResponse.model_validate(new_deck) @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("/", response_model=List[DeckResponse]) 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() return [DeckResponse.model_validate(deck) for deck in decks] @router.post("/", response_model=DeckResponse) 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, ) db.add(new_deck) await db.flush() return DeckResponse.model_validate(new_deck) @router.get("/{deck_id}", response_model=DeckResponse) 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", ) return DeckResponse.model_validate(deck) @router.patch("/{deck_id}", response_model=DeckResponse) 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() return DeckResponse.model_validate(updated_deck) @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"}