"""Deck schemas for Mantimon TCG.

This module defines Pydantic models for deck-related API requests
and responses. Decks contain card compositions for gameplay.

The backend is stateless - deck rules come from the request via DeckConfig.
Frontend provides the rules appropriate for the game mode (campaign, freeplay, custom).

Example:
    deck = DeckResponse(
        id=uuid4(),
        name="Electric Storm",
        cards={"a1-094-pikachu": 4, "a1-095-raichu": 2},
        energy_cards={"lightning": 14, "colorless": 6},
        is_valid=True,
        validation_errors=None,
        is_starter=False,
        starter_type=None,
        created_at=datetime.now(UTC),
        updated_at=datetime.now(UTC)
    )
"""

import re
from datetime import datetime
from uuid import UUID

from pydantic import BaseModel, Field, field_validator

from app.core.config import DeckConfig
from app.core.enums import EnergyType

# Card ID format: set-number-name (e.g., a1-001-bulbasaur, a1-094-pikachu-ex)
CARD_ID_PATTERN = re.compile(r"^[a-z0-9]+-\d{3}-[a-z0-9-]+$")

# Set of valid energy type names (lowercase values from EnergyType enum)
VALID_ENERGY_TYPES: frozenset[str] = frozenset(e.value for e in EnergyType)


def validate_card_quantities(cards: dict[str, int], field_name: str) -> dict[str, int]:
    """Validate card quantities are within valid range.

    Args:
        cards: Mapping of card IDs to quantities.
        field_name: Name of the field for error messages.

    Returns:
        The validated card dictionary.

    Raises:
        ValueError: If any quantity is invalid.
    """
    for card_id, quantity in cards.items():
        if quantity < 1:
            raise ValueError(
                f"{field_name}: quantity for '{card_id}' must be at least 1, got {quantity}"
            )
        if quantity > 99:
            raise ValueError(
                f"{field_name}: quantity for '{card_id}' cannot exceed 99, got {quantity}"
            )
    return cards


def validate_energy_types(energy_cards: dict[str, int], field_name: str) -> dict[str, int]:
    """Validate energy type names are valid.

    Args:
        energy_cards: Mapping of energy type names to quantities.
        field_name: Name of the field for error messages.

    Returns:
        The validated energy card dictionary.

    Raises:
        ValueError: If any energy type is invalid.
    """
    invalid_types = [et for et in energy_cards if et not in VALID_ENERGY_TYPES]
    if invalid_types:
        display_types = invalid_types[:5]
        more = len(invalid_types) - 5
        error_msg = f"{field_name}: invalid energy types: {', '.join(display_types)}"
        if more > 0:
            error_msg += f" (and {more} more)"
        error_msg += f". Valid types: {', '.join(sorted(VALID_ENERGY_TYPES))}"
        raise ValueError(error_msg)
    return energy_cards


def validate_card_id_format(cards: dict[str, int], field_name: str) -> dict[str, int]:
    """Validate card IDs match expected format.

    Args:
        cards: Mapping of card IDs to quantities.
        field_name: Name of the field for error messages.

    Returns:
        The validated card dictionary.

    Raises:
        ValueError: If any card ID has invalid format.
    """
    for card_id in cards:
        if not CARD_ID_PATTERN.match(card_id):
            raise ValueError(
                f"{field_name}: invalid card ID format '{card_id}'. "
                f"Expected format: set-number-name (e.g., a1-001-bulbasaur)"
            )
    return cards


class DeckCreateRequest(BaseModel):
    """Request model for creating a new deck.

    Attributes:
        name: Display name for the deck.
        cards: Mapping of card IDs to quantities (40 cards total by default).
        energy_cards: Mapping of energy types to quantities (20 total by default).
        deck_config: Deck rules from the frontend (defaults to standard rules).
        validate_ownership: If True, validates user owns all cards (campaign mode).
        description: Optional deck description.
    """

    name: str = Field(..., min_length=1, max_length=100, description="Deck name")
    cards: dict[str, int] = Field(..., description="Card ID to quantity mapping")
    energy_cards: dict[str, int] = Field(..., description="Energy type to quantity mapping")
    deck_config: DeckConfig = Field(
        default_factory=DeckConfig, description="Deck validation rules from frontend"
    )
    validate_ownership: bool = Field(
        default=True, description="Validate card ownership (False for freeplay)"
    )
    description: str | None = Field(
        default=None, max_length=500, description="Optional deck description"
    )

    @field_validator("cards")
    @classmethod
    def validate_cards(cls, v: dict[str, int]) -> dict[str, int]:
        """Validate card IDs and quantities."""
        validate_card_id_format(v, "cards")
        validate_card_quantities(v, "cards")
        return v

    @field_validator("energy_cards")
    @classmethod
    def validate_energy_cards(cls, v: dict[str, int]) -> dict[str, int]:
        """Validate energy card types and quantities."""
        validate_energy_types(v, "energy_cards")
        validate_card_quantities(v, "energy_cards")
        return v


class DeckUpdateRequest(BaseModel):
    """Request model for updating a deck.

    All fields are optional - only provided fields are updated.
    If cards or energy_cards change, the deck is re-validated with deck_config.

    Attributes:
        name: New display name for the deck.
        cards: New card composition.
        energy_cards: New energy composition.
        deck_config: Deck rules from the frontend (defaults to standard rules).
        validate_ownership: If True, validates user owns all cards (campaign mode).
        description: New deck description.
    """

    name: str | None = Field(
        default=None, min_length=1, max_length=100, description="New deck name"
    )
    cards: dict[str, int] | None = Field(default=None, description="New card composition")
    energy_cards: dict[str, int] | None = Field(default=None, description="New energy composition")
    deck_config: DeckConfig = Field(
        default_factory=DeckConfig, description="Deck validation rules from frontend"
    )
    validate_ownership: bool = Field(
        default=True, description="Validate card ownership (False for freeplay)"
    )
    description: str | None = Field(default=None, max_length=500, description="New description")

    @field_validator("cards")
    @classmethod
    def validate_cards(cls, v: dict[str, int] | None) -> dict[str, int] | None:
        """Validate card IDs and quantities if provided."""
        if v is not None:
            validate_card_id_format(v, "cards")
            validate_card_quantities(v, "cards")
        return v

    @field_validator("energy_cards")
    @classmethod
    def validate_energy_cards(cls, v: dict[str, int] | None) -> dict[str, int] | None:
        """Validate energy card types and quantities if provided."""
        if v is not None:
            validate_energy_types(v, "energy_cards")
            validate_card_quantities(v, "energy_cards")
        return v


class DeckResponse(BaseModel):
    """Response model for a deck.

    Includes the full deck composition and validation state.

    Attributes:
        id: Unique deck identifier.
        name: Display name of the deck.
        description: Optional deck description or notes.
        cards: Mapping of card IDs to quantities.
        energy_cards: Mapping of energy types to quantities.
        is_valid: Whether deck passes all validation rules.
        validation_errors: List of validation error messages (if any).
        is_starter: Whether this is a starter deck.
        starter_type: Type of starter deck (grass, fire, etc.) if applicable.
        created_at: When the deck was created.
        updated_at: When the deck was last modified.
    """

    id: UUID = Field(..., description="Deck ID")
    name: str = Field(..., description="Deck name")
    description: str | None = Field(default=None, description="Deck description")
    cards: dict[str, int] = Field(..., description="Card ID to quantity mapping")
    energy_cards: dict[str, int] = Field(..., description="Energy type to quantity mapping")
    is_valid: bool = Field(..., description="Whether deck is valid")
    validation_errors: list[str] | None = Field(
        default=None, description="Validation error messages"
    )
    is_starter: bool = Field(default=False, description="Is starter deck")
    starter_type: str | None = Field(default=None, description="Starter deck type")
    created_at: datetime = Field(..., description="Creation timestamp")
    updated_at: datetime = Field(..., description="Last update timestamp")

    model_config = {"from_attributes": True}


class DeckListResponse(BaseModel):
    """Response model for listing user's decks.

    Attributes:
        decks: List of user's decks.
        deck_count: Number of decks the user has.
        deck_limit: Maximum decks allowed (None for unlimited/premium).
    """

    decks: list[DeckResponse] = Field(default_factory=list, description="User's decks")
    deck_count: int = Field(..., ge=0, description="Number of decks")
    deck_limit: int | None = Field(default=None, description="Max decks (None = unlimited)")


class DeckValidateRequest(BaseModel):
    """Request model for validating a deck without saving.

    Used to check if a deck composition is valid before creating it.

    Attributes:
        cards: Card ID to quantity mapping to validate.
        energy_cards: Energy type to quantity mapping to validate.
        deck_config: Deck rules from the frontend (defaults to standard rules).
        validate_ownership: If True, validates user owns all cards (campaign mode).
    """

    cards: dict[str, int] = Field(..., description="Card ID to quantity mapping")
    energy_cards: dict[str, int] = Field(..., description="Energy type to quantity mapping")
    deck_config: DeckConfig = Field(
        default_factory=DeckConfig, description="Deck validation rules from frontend"
    )
    validate_ownership: bool = Field(
        default=True, description="Validate card ownership (False for freeplay)"
    )

    @field_validator("cards")
    @classmethod
    def validate_cards(cls, v: dict[str, int]) -> dict[str, int]:
        """Validate card IDs and quantities."""
        validate_card_id_format(v, "cards")
        validate_card_quantities(v, "cards")
        return v

    @field_validator("energy_cards")
    @classmethod
    def validate_energy_cards(cls, v: dict[str, int]) -> dict[str, int]:
        """Validate energy card types and quantities."""
        validate_energy_types(v, "energy_cards")
        validate_card_quantities(v, "energy_cards")
        return v


class DeckValidationResponse(BaseModel):
    """Response model for deck validation results.

    Attributes:
        is_valid: Whether the deck passes all validation rules.
        errors: List of validation error messages.
    """

    is_valid: bool = Field(..., description="Whether deck is valid")
    errors: list[str] = Field(default_factory=list, description="Validation errors")


class StarterDeckSelectRequest(BaseModel):
    """Request model for selecting a starter deck.

    Attributes:
        starter_type: Type of starter deck to select.
        deck_config: Deck rules from the frontend (defaults to standard rules).
    """

    starter_type: str = Field(
        ...,
        description="Starter deck type (grass, fire, water, psychic, lightning)",
    )
    deck_config: DeckConfig = Field(
        default_factory=DeckConfig, description="Deck validation rules from frontend"
    )


class StarterStatusResponse(BaseModel):
    """Response model for starter deck status.

    Attributes:
        has_starter: Whether user has selected a starter deck.
        starter_type: Type of starter deck selected (if any).
    """

    has_starter: bool = Field(..., description="Has starter been selected")
    starter_type: str | None = Field(default=None, description="Selected starter type")
