"""Deck manager service."""
from typing import List, Dict, Any, Optional
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, update, delete
from sqlalchemy.orm import selectinload

from app.models.user_deck import UserDeck, UserDeckCard, DeckPrecedent, DeckPrecedentCard
from app.models.models import MtgonlineCard


class DeckManager:
    """Deck manager service."""

    @staticmethod
    async def create_deck(
        db: AsyncSession,
        user_id: int,
        name: str,
        folder_id: Optional[int] = None,
        format: str = "standard",
        notes: Optional[str] = None,
        is_precedent: bool = False,
        precedent_name: Optional[str] = None,
    ) -> UserDeck:
        """Create a new deck."""
        deck = UserDeck(
            user_id=user_id,
            name=name,
            folder_id=folder_id,
            format=format,
            notes=notes,
            is_precedent=is_precedent,
            precedent_name=precedent_name,
        )
        db.add(deck)
        await db.flush()
        return deck

    @staticmethod
    async def get_deck(db: AsyncSession, deck_id: int, user_id: int) -> Optional[UserDeck]:
        """Get a deck by ID."""
        stmt = select(UserDeck).where(UserDeck.id == deck_id, UserDeck.user_id == user_id)
        result = await db.execute(stmt)
        return result.scalar_one_or_none()

    @staticmethod
    async def list_decks(
        db: AsyncSession,
        user_id: int,
        status_filter: Optional[str] = None,
        folder_id: Optional[int] = None,
        is_precedent: Optional[bool] = None,
        page: int = 1,
        page_size: int = 50,
    ) -> List[UserDeck]:
        """List user's decks with filtering."""
        conditions = [UserDeck.user_id == user_id]
        if status_filter:
            conditions.append(UserDeck.status == status_filter)
        if folder_id:
            conditions.append(UserDeck.folder_id == folder_id)
        if is_precedent is not None:
            conditions.append(UserDeck.is_precedent == is_precedent)

        offset = (page - 1) * page_size
        stmt = select(UserDeck).where(*conditions).order_by(UserDeck.updated_at.desc()).offset(offset).limit(page_size)
        result = await db.execute(stmt)
        return result.scalars().all()

    @staticmethod
    async def update_deck(
        db: AsyncSession,
        deck_id: int,
        user_id: int,
        name: Optional[str] = None,
        folder_id: Optional[int] = None,
        format: Optional[str] = None,
        notes: Optional[str] = None,
    ) -> Optional[UserDeck]:
        """Update a deck."""
        deck = await DeckManager.get_deck(db, deck_id, user_id)
        if not deck:
            return None

        if deck.status == "FINAL":
            raise ValueError("Cannot modify a finalized deck")

        if name:
            deck.name = name
        if folder_id is not None:
            deck.folder_id = folder_id
        if format:
            deck.format = format
        if notes is not None:
            deck.notes = notes

        await db.flush()
        return deck

    @staticmethod
    async def delete_deck(db: AsyncSession, deck_id: int, user_id: int) -> bool:
        """Delete a deck."""
        deck = await DeckManager.get_deck(db, deck_id, user_id)
        if not deck:
            return False

        await db.execute(delete(UserDeck).where(UserDeck.id == deck_id))
        await db.flush()
        return True

    @staticmethod
    async def finalize_deck(db: AsyncSession, deck_id: int, user_id: int) -> Optional[UserDeck]:
        """Transition a deck from DRAFT to FINAL status."""
        deck = await DeckManager.get_deck(db, deck_id, user_id)
        if not deck:
            return None

        if deck.status == "FINAL":
            raise ValueError("Deck is already finalized")

        # Check deck has cards
        card_count_stmt = select(func.count()).select_from(UserDeckCard).where(UserDeckCard.deck_id == deck_id)
        card_count_result = await db.execute(card_count_stmt)
        card_count = card_count_result.scalar() or 0
        if card_count == 0:
            raise ValueError("Cannot finalize an empty deck")

        deck.status = "FINAL"
        await db.flush()
        return deck

    @staticmethod
    async def add_card_to_deck(
        db: AsyncSession,
        deck_id: int,
        card_id: int,
        quantity: int = 1,
        zone: str = "main",
        position: Optional[int] = None,
    ) -> UserDeckCard:
        """Add a card to a deck."""
        deck_card = UserDeckCard(
            deck_id=deck_id,
            card_id=card_id,
            quantity=quantity,
            zone=zone,
            position=position,
        )
        db.add(deck_card)
        await db.flush()
        return deck_card

    @staticmethod
    async def get_deck_cards(db: AsyncSession, deck_id: int, zone: Optional[str] = None) -> List[UserDeckCard]:
        """Get cards in a deck."""
        conditions = [UserDeckCard.deck_id == deck_id]
        if zone:
            conditions.append(UserDeckCard.zone == zone)

        stmt = select(UserDeckCard).where(*conditions).order_by(UserDeckCard.id)
        result = await db.execute(stmt)
        return result.scalars().all()

    @staticmethod
    async def update_deck_card(
        db: AsyncSession,
        deck_card_id: int,
        quantity: Optional[int] = None,
        zone: Optional[str] = None,
        position: Optional[int] = None,
    ) -> Optional[UserDeckCard]:
        """Update a card in a deck."""
        stmt = select(UserDeckCard).where(UserDeckCard.id == deck_card_id)
        result = await db.execute(stmt)
        deck_card = result.scalar_one_or_none()

        if not deck_card:
            return None

        if quantity is not None:
            deck_card.quantity = quantity
        if zone:
            deck_card.zone = zone
        if position is not None:
            deck_card.position = position

        await db.flush()
        return deck_card

    @staticmethod
    async def remove_card_from_deck(db: AsyncSession, deck_card_id: int) -> bool:
        """Remove a card from a deck."""
        stmt = select(UserDeckCard).where(UserDeckCard.id == deck_card_id)
        result = await db.execute(stmt)
        deck_card = result.scalar_one_or_none()

        if not deck_card:
            return False

        await db.execute(delete(UserDeckCard).where(UserDeckCard.id == deck_card_id))
        await db.flush()
        return True

    @staticmethod
    async def clone_precedent(
        db: AsyncSession,
        precedent_id: int,
        user_id: int,
        name: Optional[str] = None,
    ) -> UserDeck:
        """Clone a precedent into a new deck."""
        # Get precedent
        stmt = select(DeckPrecedent).where(DeckPrecedent.id == precedent_id)
        result = await db.execute(stmt)
        precedent = result.scalar_one_or_none()

        if not precedent:
            raise ValueError(f"Precedent {precedent_id} not found")

        # Create new deck
        new_name = name or f"Copy of {precedent.name}"
        new_deck = UserDeck(
            user_id=user_id,
            name=new_name,
            format=precedent.format,
            is_precedent=False,
        )
        db.add(new_deck)
        await db.flush()

        # Copy cards from precedent
        card_stmt = select(DeckPrecedentCard).where(DeckPrecedentCard.precedent_id == precedent_id)
        card_result = await db.execute(card_stmt)
        precedent_cards = card_result.scalars().all()

        for pc in precedent_cards:
            new_dc = UserDeckCard(
                deck_id=new_deck.id,
                card_id=pc.card_id,
                quantity=pc.quantity,
                zone=pc.zone,
            )
            db.add(new_dc)

        await db.flush()
        return new_deck
