"""
Card Mirror Service

Manages mirrored card data in mtgo_platform for fast deckbuilding queries.
Syncs with mtg_cards (mtg_data) when the card database is refreshed.
"""
from typing import List, Dict, Any, Optional, Tuple
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func, update, delete
from sqlalchemy.orm import selectinload

from app.models.mtg_models import MtgCard, MtgSet
from app.models.mirror_models import MtgCardMirror, DeckCardLink
from app.core.database import mirror_get_db


async def upsert_card_mirror(
    db: AsyncSession,
    card_data: Dict[str, Any],
    source_id: Optional[int] = None
) -> MtgCardMirror:
    """
    Upsert a card into the mirror table.
    
    Args:
        db: Database session
        card_data: Card data dictionary
        source_id: Optional reference to mtg_cards.id
    
    Returns:
        The upserted MtgCardMirror instance
    """
    # Check if card already exists by name
    existing = await db.execute(
        select(MtgCardMirror).where(MtgCardMirror.name == card_data.get("name"))
    )
    existing_card = existing.scalar_one_or_none()
    
    if existing_card:
        # Update existing mirror
        for key, value in card_data.items():
            if hasattr(existing_card, key):
                setattr(existing_card, key, value)
        if source_id:
            existing_card.source_id = source_id
    else:
        # Create new mirror
        mirror_data = {
            "name": card_data.get("name"),
            "mana_cost": card_data.get("mana_cost"),
            "type_line": card_data.get("type_line"),
            "oracle_text": card_data.get("oracle_text"),
            "power": card_data.get("power"),
            "toughness": card_data.get("toughness"),
            "rarity": card_data.get("rarity"),
            "layout": card_data.get("layout"),
            "artist": card_data.get("artist"),
            "flavor_text": card_data.get("flavor_text"),
            "numbers": card_data.get("numbers"),
            "identifiers": card_data.get("identifiers"),
            "images": card_data.get("images"),
            "image": card_data.get("image"),
            "card_parts": card_data.get("card_parts"),
            "keywords": card_data.get("keywords"),
            "legalities": card_data.get("legalities"),
            "set_code": card_data.get("set_code"),
            "set_name": card_data.get("set_name"),
            "source_id": source_id,
        }
        mirror_card = MtgCardMirror(**mirror_data)
        db.add(mirror_card)
        await db.flush()
        return mirror_card
    
    return existing_card


async def get_card_mirror_by_name(
    db: AsyncSession,
    name: str
) -> Optional[MtgCardMirror]:
    """
    Get a mirrored card by name.
    
    Args:
        db: Database session
        name: Card name
    
    Returns:
        MtgCardMirror instance or None
    """
    result = await db.execute(
        select(MtgCardMirror).where(MtgCardMirror.name == name)
    )
    return result.scalar_one_or_none()


async def search_card_mirrors(
    db: AsyncSession,
    query: str,
    limit: int = 100,
    offset: int = 0
) -> Tuple[List[MtgCardMirror], int]:
    """
    Search mirrored cards by name.
    
    Args:
        db: Database session
        query: Search query
        limit: Maximum results
        offset: Pagination offset
    
    Returns:
        Tuple of (list of mirrors, total count)
    """
    search_term = f"%{query.lower()}%"
    
    # Search query
    stmt = (
        select(MtgCardMirror)
        .where(MtgCardMirror.name.ilike(search_term))
        .offset(offset)
        .limit(limit)
    )
    
    result = await db.execute(stmt)
    mirrors = result.scalars().all()
    
    # Total count
    count_stmt = select(func.count()).select_from(MtgCardMirror).where(
        MtgCardMirror.name.ilike(search_term)
    )
    count_result = await db.execute(count_stmt)
    total = count_result.scalar()
    
    return mirrors, total


async def add_card_to_deck(
    db: AsyncSession,
    deck_id: int,
    card_id: int,
    quantity: int = 1,
    zone: str = "main"
) -> DeckCardLink:
    """
    Add a card to a deck.
    
    Args:
        db: Database session
        deck_id: Deck ID
        card_id: Mirrored card ID
        quantity: Number of copies
        zone: 'main' or 'sideboard'
    
    Returns:
        DeckCardLink instance
    """
    # Check if link already exists
    existing = await db.execute(
        select(DeckCardLink).where(
            DeckCardLink.deck_id == deck_id,
            DeckCardLink.card_id == card_id,
            DeckCardLink.zone == zone
        )
    )
    existing_link = existing.scalar_one_or_none()
    
    if existing_link:
        # Update quantity
        existing_link.quantity = quantity
        await db.flush()
        return existing_link
    else:
        # Create new link
        link = DeckCardLink(
            deck_id=deck_id,
            card_id=card_id,
            quantity=quantity,
            zone=zone
        )
        db.add(link)
        await db.flush()
        return link


async def remove_card_from_deck(
    db: AsyncSession,
    deck_id: int,
    card_id: int,
    zone: str = "main"
) -> bool:
    """
    Remove a card from a deck.
    
    Args:
        db: Database session
        deck_id: Deck ID
        card_id: Mirrored card ID
        zone: 'main' or 'sideboard'
    
    Returns:
        True if removed, False if not found
    """
    result = await db.execute(
        delete(DeckCardLink).where(
            DeckCardLink.deck_id == deck_id,
            DeckCardLink.card_id == card_id,
            DeckCardLink.zone == zone
        )
    )
    return result.rowcount > 0


async def get_deck_cards(
    db: AsyncSession,
    deck_id: int
) -> List[DeckCardLink]:
    """
    Get all cards in a deck with their mirrored data.
    
    Args:
        db: Database session
        deck_id: Deck ID
    
    Returns:
        List of DeckCardLink instances with joined card data
    """
    stmt = (
        select(DeckCardLink, MtgCardMirror)
        .join(MtgCardMirror, DeckCardLink.card_id == MtgCardMirror.id)
        .where(DeckCardLink.deck_id == deck_id)
        .order_by(DeckCardLink.id)
    )
    
    result = await db.execute(stmt)
    rows = result.all()
    
    links = []
    for link, card in rows:
        link.card = card
        links.append(link)
    
    return links


async def get_user_deck_summaries(
    db: AsyncSession,
    user_id: int
) -> List[Dict[str, Any]]:
    """
    Get all decks for a user with card counts.
    
    Args:
        db: Database session
        user_id: User ID
    
    Returns:
        List of deck summaries with card counts
    """
    from app.models.models import DecklistFile
    
    stmt = (
        select(DecklistFile)
        .where(DecklistFile.owner_id == user_id)
        .order_by(DecklistFile.creation_date.desc())
    )
    
    result = await db.execute(stmt)
    decks = result.scalars().all()
    
    summaries = []
    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()
        
        summaries.append({
            "id": deck.id,
            "name": deck.name,
            "format": deck.format,
            "status": deck.status,
            "folder_id": deck.folder_id,
            "owner_id": deck.owner_id,
            "creation_date": deck.creation_date,
            "card_count": card_count,
        })
    
    return summaries


async def sync_mirrors_from_mtg_cards(
    db: AsyncSession,
    mtg_db: Optional[AsyncSession] = None
) -> int:
    """
    Sync all mirrored cards from the mtg_cards table.
    
    This is called when the card database is refreshed.
    
    Args:
        db: Mirror database session
        mtg_db: Optional MTG database session for cross-DB queries
    
    Returns:
        Number of cards synced
    """
    if not mtg_db:
        # Use the same session if no MTG session provided
        # Note: In production, you'd need a cross-DB connection
        # For now, we'll just refresh the mirror from existing data
        pass
    
    # For now, this is a no-op. In a full implementation,
    # you'd query mtg_cards and upsert into mtg_cards_mirror.
    # This requires cross-database connections which SQLAlchemy
    # can handle with proper configuration.
    
    return 0


async def get_card_statistics(db: AsyncSession) -> Dict[str, Any]:
    """
    Get statistics about mirrored cards.
    
    Args:
        db: Database session
    
    Returns:
        Dictionary with statistics
    """
    # Total mirrored cards
    total_stmt = select(func.count()).select_from(MtgCardMirror)
    total = (await db.execute(total_stmt)).scalar()
    
    # Cards by rarity
    rarity_stmt = select(MtgCardMirror.rarity, func.count()).group_by(
        MtgCardMirror.rarity
    )
    rarity_result = await db.execute(rarity_stmt)
    rarities = {row[0]: row[1] for row in rarity_result if row[0]}
    
    # Cards by type
    type_stmt = select(MtgCardMirror.type_line, func.count()).group_by(
        MtgCardMirror.type_line
    )
    type_result = await db.execute(type_stmt)
    types = {row[0]: row[1] for row in type_result if row[0]}
    
    return {
        "total_mirrored_cards": total,
        "cards_by_rarity": rarities,
        "cards_by_type": types,
    }
