""" Card search router for MTG card database. Provides endpoints for searching and retrieving MTG card data with filters for type, set, and color. """ from typing import List, Optional, Dict, Any from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select from app.core.database import mtg_get_db from app.core.redis_client import cache_get, cache_set from app.services.card_search_service import CardSearchService from app.services.deck_suggestion_service import DeckSuggestionService from app.models.user_deck import UserDeck from app.schemas.card_search_schemas import CardSearchResponse, CardResponse, SetResponse, CardTypeResponse router = APIRouter(prefix="/api/cards", tags=["Card Search"]) @router.get("/search", response_model=CardSearchResponse) async def search_cards_endpoint( q: str = Query(..., min_length=1, description="Search query"), card_type: Optional[str] = Query(None, description="Filter by card type"), set_code: Optional[str] = Query(None, description="Filter by set code"), color: Optional[str] = Query(None, description="Filter by color (e.g., WU, BR)"), limit: int = Query(100, ge=1, le=500, description="Maximum results"), offset: int = Query(0, ge=0, description="Number of results to skip"), db: AsyncSession = Depends(mtg_get_db), ): """ Search cards with filters. Supports filtering by type, set, and color in addition to name search. """ cache_key = f"card_search:{q}:{card_type}:{set_code}:{color}:{limit}:{offset}" # Check cache first cached = await cache_get(cache_key) if cached: return {"cached": True, "results": cached} # Search cards results = await CardSearchService.search_cards( db=db, query=q, card_type=card_type, set_code=set_code, color=color, limit=limit, offset=offset, ) # Cache results for 5 minutes await cache_set(cache_key, str(results), ttl=300) return {"cached": False, "results": results} @router.get("/{card_id}", response_model=CardResponse) async def get_card_endpoint( card_id: int, db: AsyncSession = Depends(mtg_get_db), ): """ Get a specific card by ID. """ cache_key = f"card_by_id:{card_id}" # Check cache first cached = await cache_get(cache_key) if cached: return {"cached": True, "results": cached} # Get card card = await CardSearchService.get_card_by_id(db, card_id) if not card: raise HTTPException(status_code=404, detail="Card not found") # Cache for 10 minutes await cache_set(cache_key, str(card), ttl=600) return {"cached": False, "results": card} @router.get("/sets", response_model=List[SetResponse]) async def get_sets_endpoint( db: AsyncSession = Depends(mtg_get_db), ): """ Get all available sets. """ cache_key = "all_sets:all" cached = await cache_get(cache_key) if cached: return {"cached": True, "results": cached} sets = await CardSearchService.get_sets(db) # Cache for 1 hour await cache_set(cache_key, str(sets), ttl=3600) return {"cached": False, "results": sets} @router.get("/types", response_model=List[CardTypeResponse]) async def get_card_types_endpoint( db: AsyncSession = Depends(mtg_get_db), ): """ Get all unique card types. """ cache_key = "card_types:all" cached = await cache_get(cache_key) if cached: return {"cached": True, "results": cached} types = await CardSearchService.get_card_types(db) # Cache for 30 minutes await cache_set(cache_key, str(types), ttl=1800) return {"cached": False, "results": types} @router.get("/rarities", response_model=List[str]) async def get_card_rarities_endpoint( db: AsyncSession = Depends(mtg_get_db), ): """ Get all unique card rarities. """ cache_key = "card_rarities:all" cached = await cache_get(cache_key) if cached: return {"cached": True, "results": cached} rarities = await CardSearchService.get_card_rarities(db) # Cache for 30 minutes await cache_set(cache_key, str(rarities), ttl=1800) return {"cached": False, "results": rarities} @router.get("/suggest", response_model=List[Dict[str, Any]]) async def suggest_cards_endpoint( deck_id: int = Query(..., description="Deck ID to suggest cards for"), limit: int = Query(20, ge=1, le=100, description="Maximum suggestions"), db: AsyncSession = Depends(mtg_get_db), ): """ Suggest similar cards for a deck. Matches by: same type, same color, same set, same mana cost, and cards often paired in existing user decks. """ # Verify deck exists stmt = select(UserDeck).where(UserDeck.id == deck_id) result = await db.execute(stmt) deck = result.scalar_one_or_none() if not deck: raise HTTPException(status_code=404, detail="Deck not found") suggestions = await DeckSuggestionService.suggest_cards(db, deck_id, limit) return suggestions