"""Pydantic models for roster link type safety

Provides league-specific type-safe models for roster operations:
- PdRosterLinkData: PD league card-based rosters
- SbaRosterLinkData: SBA league player-based rosters
"""

from abc import ABC, abstractmethod
from uuid import UUID

from pydantic import BaseModel, ConfigDict, computed_field, field_validator


class BaseRosterLinkData(BaseModel, ABC):
    """Abstract base for roster link data

    Common fields shared across all leagues
    """

    model_config = ConfigDict(from_attributes=True, arbitrary_types_allowed=True)

    id: int | None = None  # Database ID (populated after save)
    game_id: UUID
    team_id: int

    @abstractmethod
    def get_entity_id(self) -> int:
        """Get the entity ID (card_id or player_id)"""
        pass

    @abstractmethod
    def get_entity_type(self) -> str:
        """Get entity type identifier ('card' or 'player')"""
        pass


class PdRosterLinkData(BaseRosterLinkData):
    """PD league roster link - tracks cards

    Used for Paper Dynasty league games where rosters are composed of cards.
    Each card represents a player with detailed scouting data.
    """

    card_id: int

    @field_validator("card_id")
    @classmethod
    def validate_card_id(cls, v: int) -> int:
        if v <= 0:
            raise ValueError("card_id must be positive")
        return v

    def get_entity_id(self) -> int:
        return self.card_id

    def get_entity_type(self) -> str:
        return "card"


class SbaRosterLinkData(BaseRosterLinkData):
    """SBA league roster link - tracks players

    Used for SBA league games where rosters are composed of players.
    Players are identified directly by player_id without a card system.

    The player_positions field stores the player's natural positions from the API
    for use in substitution UI filtering (batters vs pitchers). This supports
    two-way players like Shohei Ohtani who have both pitching and batting positions.

    The player_data field caches essential SbaPlayer fields (name, image, headshot)
    to avoid runtime API calls when loading bench players.
    """

    player_id: int
    player_positions: list[str] = []
    player_data: dict | None = None  # Cached SbaPlayer fields: {name, image, headshot}

    @field_validator("player_id")
    @classmethod
    def validate_player_id(cls, v: int) -> int:
        if v <= 0:
            raise ValueError("player_id must be positive")
        return v

    def get_entity_id(self) -> int:
        return self.player_id

    def get_entity_type(self) -> str:
        return "player"

    # Pitcher positions used for filtering
    # CP = Closer Pitcher (alternate notation for CL)
    _PITCHER_POSITIONS: set[str] = {"P", "SP", "RP", "CL", "CP"}

    @computed_field
    @property
    def is_pitcher(self) -> bool:
        """True if player has any pitching position (supports two-way players)

        Examples:
        - ["SP"] -> True
        - ["SP", "DH"] -> True (two-way player like Ohtani)
        - ["CF", "DH"] -> False
        """
        return any(pos in self._PITCHER_POSITIONS for pos in self.player_positions)

    @computed_field
    @property
    def is_batter(self) -> bool:
        """True if player has any non-pitching position (supports two-way players)

        Examples:
        - ["CF", "DH"] -> True
        - ["SP", "DH"] -> True (two-way player like Ohtani)
        - ["SP"] -> False (pitcher-only)
        """
        return any(pos not in self._PITCHER_POSITIONS for pos in self.player_positions)


class RosterLinkCreate(BaseModel):
    """Request model for creating a roster link"""

    game_id: UUID
    team_id: int
    card_id: int | None = None
    player_id: int | None = None
    player_positions: list[str] = []  # Natural positions for substitution filtering

    @field_validator("team_id")
    @classmethod
    def validate_team_id(cls, v: int) -> int:
        if v <= 0:
            raise ValueError("team_id must be positive")
        return v

    def model_post_init(self, __context) -> None:
        """Validate that exactly one ID is populated"""
        has_card = self.card_id is not None
        has_player = self.player_id is not None

        if has_card == has_player:  # XOR check (both True or both False = invalid)
            raise ValueError("Exactly one of card_id or player_id must be provided")

    def to_pd_data(self) -> PdRosterLinkData:
        """Convert to PD roster data (validates card_id is present)"""
        if self.card_id is None:
            raise ValueError("card_id required for PD roster")
        return PdRosterLinkData(
            game_id=self.game_id, team_id=self.team_id, card_id=self.card_id
        )

    def to_sba_data(self) -> SbaRosterLinkData:
        """Convert to SBA roster data (validates player_id is present)"""
        if self.player_id is None:
            raise ValueError("player_id required for SBA roster")
        return SbaRosterLinkData(
            game_id=self.game_id,
            team_id=self.team_id,
            player_id=self.player_id,
            player_positions=self.player_positions,
        )
