"""Game rules configuration for Mantimon TCG.

This module defines the master configuration system for all game rules. The engine
is highly configurable to support both campaign mode (with fixed rules) and free
play mode (with user-adjustable rules).

Default values are based on the Mantimon TCG house rules documented in GAME_RULES.md,
which use a Pokemon Pocket-inspired energy system with a 40-card main deck and
separate 20-card energy deck.

Usage:
    # Use default rules
    rules = RulesConfig()

    # Customize specific rules
    rules = RulesConfig(
        deck=DeckConfig(min_size=60, max_size=60),
        prizes=PrizeConfig(count=6),
    )

    # Load from JSON
    rules = RulesConfig.model_validate_json(json_string)
"""

from pydantic import BaseModel, Field

from app.core.enums import EnergyType, ModifierMode, PokemonVariant


class DeckConfig(BaseModel):
    """Configuration for deck building rules.

    Attributes:
        min_size: Minimum number of cards in the main deck.
        max_size: Maximum number of cards in the main deck.
        exact_size_required: If True, deck must be exactly min_size cards.
        max_copies_per_card: Maximum copies of any single card (by name).
        max_copies_basic_energy: Max copies of basic energy in energy deck.
            None means unlimited.
        min_basic_pokemon: Minimum number of Basic Pokemon required.
        energy_deck_enabled: If True, use separate energy deck (Pokemon Pocket style).
        energy_deck_size: Size of the separate energy deck.
        starting_hand_size: Number of cards drawn at game start.
    """

    min_size: int = 40
    max_size: int = 40
    exact_size_required: bool = True
    max_copies_per_card: int = 4
    max_copies_basic_energy: int | None = None
    min_basic_pokemon: int = 1
    energy_deck_enabled: bool = True
    energy_deck_size: int = 20
    starting_hand_size: int = 7


class ActiveConfig(BaseModel):
    """Configuration for active Pokemon slot rules.

    Supports standard single-battle (1 active) or double-battle variants
    (2 active Pokemon per player).

    Attributes:
        max_active: Maximum number of Pokemon in the active position.
            Default is 1 (standard single battle). Set to 2 for double battles.
    """

    max_active: int = 1


class BenchConfig(BaseModel):
    """Configuration for bench rules.

    Attributes:
        max_size: Maximum number of Pokemon on the bench.
    """

    max_size: int = 5


class EnergyConfig(BaseModel):
    """Configuration for energy attachment rules.

    Attributes:
        attachments_per_turn: Number of energy cards that can be attached per turn.
        types_enabled: List of energy types available in this game.
        auto_flip_from_deck: If True, flip top card of energy deck at turn start
            (Pokemon Pocket style).
    """

    attachments_per_turn: int = 1
    types_enabled: list[EnergyType] = Field(default_factory=lambda: list(EnergyType))
    auto_flip_from_deck: bool = True


class PrizeConfig(BaseModel):
    """Configuration for prize/scoring rules.

    In core Mantimon TCG rules, "prizes" are replaced with "points" - players
    score points instead of taking prize cards. This simplifies the game while
    maintaining the knockout scoring mechanic.

    Knockout points are determined by the Pokemon's variant (EX, V, GX, etc.). A Basic EX is worth the same as a Stage 2 EX.

    Attributes:
        count: Number of points needed to win (or prize cards if using classic rules).
        per_knockout_normal: Points for knocking out a normal (non-variant) Pokemon.
        per_knockout_ex: Points scored for knocking out an EX Pokemon.
        per_knockout_gx: Points scored for knocking out a GX Pokemon.
        per_knockout_v: Points scored for knocking out a V Pokemon.
        per_knockout_vmax: Points scored for knocking out a VMAX Pokemon.
        per_knockout_vstar: Points scored for knocking out a VSTAR Pokemon (3 points).
        use_prize_cards: If True, use classic prize card mechanic instead of points.
        prize_selection_random: If True, prize cards are taken randomly (classic).
            If False, player chooses which prize to take.
    """

    count: int = 4
    per_knockout_normal: int = 1
    per_knockout_ex: int = 2
    per_knockout_gx: int = 2
    per_knockout_v: int = 2
    per_knockout_vmax: int = 3
    per_knockout_vstar: int = 3
    use_prize_cards: bool = False
    prize_selection_random: bool = True

    def points_for_knockout(self, variant: PokemonVariant) -> int:
        """Get the number of points scored for knocking out a Pokemon of the given variant.

        Args:
            variant: The PokemonVariant of the knocked out Pokemon.

        Returns:
            Number of points to score.
        """
        variant_map = {
            PokemonVariant.NORMAL: self.per_knockout_normal,
            PokemonVariant.EX: self.per_knockout_ex,
            PokemonVariant.GX: self.per_knockout_gx,
            PokemonVariant.V: self.per_knockout_v,
            PokemonVariant.VMAX: self.per_knockout_vmax,
            PokemonVariant.VSTAR: self.per_knockout_vstar,
        }
        return variant_map.get(variant, self.per_knockout_normal)


class FirstTurnConfig(BaseModel):
    """Configuration for first turn restrictions.

    These rules apply only to the very first turn of the game (turn 1 for player 1).

    Attributes:
        can_draw: Whether the first player draws a card on turn 1.
        can_attack: Whether the first player can attack on turn 1.
        can_play_supporter: Whether the first player can play Supporter cards on turn 1.
        can_attach_energy: Whether the first player can attach energy on turn 1.
        can_evolve: Whether the first player can evolve Pokemon on turn 1.
    """

    can_draw: bool = True
    can_attack: bool = True
    can_play_supporter: bool = True
    can_attach_energy: bool = False
    can_evolve: bool = False


class WinConditionsConfig(BaseModel):
    """Configuration for win/loss conditions.

    Each condition can be enabled or disabled independently. A player wins
    when any enabled win condition is met.

    Attributes:
        all_prizes_taken: Win when a player scores the required number of points.
        no_pokemon_in_play: Win when opponent has no Pokemon in play.
        cannot_draw: Win when opponent cannot draw a card at turn start.
        turn_limit_enabled: Enable maximum turn count (useful for AI matches).
        turn_limit: Maximum number of turns before game ends. Each player's
            turn counts as one turn (so 30 = 15 turns per player).
        turn_timer_enabled: Enable per-turn time limits (multiplayer).
        turn_timer_seconds: Seconds per turn before timeout (default 90).
        turn_timer_warning_thresholds: Percentage of time remaining to send warnings.
            Default [50, 25] means warnings at 50% and 25% remaining.
        turn_timer_grace_seconds: Extra seconds granted on reconnection.
        game_timer_enabled: Enable total game time limit (multiplayer).
        game_timer_minutes: Total game time in minutes.
    """

    all_prizes_taken: bool = True
    no_pokemon_in_play: bool = True
    cannot_draw: bool = True
    turn_limit_enabled: bool = True
    turn_limit: int = 30
    turn_timer_enabled: bool = False
    turn_timer_seconds: int = 90
    turn_timer_warning_thresholds: list[int] = Field(default_factory=lambda: [50, 25])
    turn_timer_grace_seconds: int = 15
    game_timer_enabled: bool = False
    game_timer_minutes: int = 30


class StatusConfig(BaseModel):
    """Configuration for status condition effects.

    Defines the damage values and removal mechanics for each status condition.

    Attributes:
        poison_damage: Damage dealt by Poison between turns.
        burn_damage: Damage dealt by Burn between turns.
        burn_flip_to_remove: If True, flip coin between turns; heads removes Burn.
        sleep_flip_to_wake: If True, flip coin between turns; heads removes Sleep.
        confusion_self_damage: Damage dealt to self on failed confusion flip.
    """

    poison_damage: int = 10
    burn_damage: int = 20
    burn_flip_to_remove: bool = True
    sleep_flip_to_wake: bool = True
    confusion_self_damage: int = 30


class TrainerConfig(BaseModel):
    """Configuration for Trainer card rules.

    Attributes:
        supporters_per_turn: Maximum Supporter cards playable per turn.
        stadiums_per_turn: Maximum Stadium cards playable per turn.
        items_per_turn: Maximum Item cards per turn. None means unlimited.
        tools_per_pokemon: Maximum Tool cards attachable to one Pokemon.
        stadium_same_name_replace: If True, a stadium can replace another stadium
            with the same name. If False (default), you cannot play a stadium if
            a stadium with the same name is already in play. Standard Pokemon TCG
            rules prohibit same-name stadium replacement.
    """

    supporters_per_turn: int = 1
    stadiums_per_turn: int = 1
    items_per_turn: int | None = None
    tools_per_pokemon: int = 1
    stadium_same_name_replace: bool = False


class EvolutionConfig(BaseModel):
    """Configuration for evolution rules.

    Attributes:
        same_turn_as_played: Can evolve a Pokemon the same turn it was played.
        same_turn_as_evolution: Can evolve a Pokemon the same turn it evolved.
        first_turn_of_game: Can evolve on the very first turn of the game.
    """

    same_turn_as_played: bool = False
    same_turn_as_evolution: bool = False
    first_turn_of_game: bool = False


class RetreatConfig(BaseModel):
    """Configuration for retreat rules.

    Attributes:
        retreats_per_turn: Maximum number of retreats allowed per turn.
        free_retreat_cost: If True, retreating doesn't require discarding energy.
    """

    retreats_per_turn: int = 1
    free_retreat_cost: bool = False


class CombatConfig(BaseModel):
    """Configuration for combat damage calculations.

    Controls how weakness and resistance modify damage. Standard Pokemon TCG uses
    multiplicative weakness (x2) and additive resistance (-30), but these can be
    customized for house rules or game variants.

    Cards can override these defaults via their WeaknessResistance definitions.

    Attributes:
        weakness_mode: How weakness modifies damage (multiplicative or additive).
        weakness_value: Default weakness modifier value.
            - For multiplicative: damage * value (e.g., 2 for x2)
            - For additive: damage + value (e.g., 20 for +20)
        resistance_mode: How resistance modifies damage (multiplicative or additive).
        resistance_value: Default resistance modifier value.
            - For multiplicative: damage * value (e.g., 0.5 for half damage)
            - For additive: damage + value (e.g., -30 for -30 damage)

    Examples:
        Standard Pokemon TCG (x2 weakness, -30 resistance):
            CombatConfig(
                weakness_mode=ModifierMode.MULTIPLICATIVE,
                weakness_value=2,
                resistance_mode=ModifierMode.ADDITIVE,
                resistance_value=-30,
            )

        Additive weakness/resistance (+20/-20):
            CombatConfig(
                weakness_mode=ModifierMode.ADDITIVE,
                weakness_value=20,
                resistance_mode=ModifierMode.ADDITIVE,
                resistance_value=-20,
            )
    """

    weakness_mode: ModifierMode = ModifierMode.MULTIPLICATIVE
    weakness_value: int = 2
    resistance_mode: ModifierMode = ModifierMode.ADDITIVE
    resistance_value: int = -30


class RulesConfig(BaseModel):
    """Master configuration for all game rules.

    This is the top-level configuration object that contains all rule settings.
    Default values are based on Mantimon TCG house rules (Pokemon Pocket-inspired
    with 40-card decks, separate energy deck, and 4 points to win).

    For standard Pokemon TCG rules, override with:
        RulesConfig(
            deck=DeckConfig(min_size=60, max_size=60, energy_deck_enabled=False),
            prizes=PrizeConfig(count=6, use_prize_cards=True),
            first_turn=FirstTurnConfig(can_attack=False, can_attach_energy=True),
        )

    Attributes:
        deck: Deck building configuration.
        active: Active Pokemon slot configuration.
        bench: Bench configuration.
        energy: Energy attachment configuration.
        prizes: Prize/scoring configuration.
        first_turn: First turn restrictions.
        win_conditions: Win/loss condition configuration.
        status: Status condition effect configuration.
        trainer: Trainer card rule configuration.
        evolution: Evolution rule configuration.
        retreat: Retreat rule configuration.
        combat: Combat damage calculation configuration.
    """

    deck: DeckConfig = Field(default_factory=DeckConfig)
    active: ActiveConfig = Field(default_factory=ActiveConfig)
    bench: BenchConfig = Field(default_factory=BenchConfig)
    energy: EnergyConfig = Field(default_factory=EnergyConfig)
    prizes: PrizeConfig = Field(default_factory=PrizeConfig)
    first_turn: FirstTurnConfig = Field(default_factory=FirstTurnConfig)
    win_conditions: WinConditionsConfig = Field(default_factory=WinConditionsConfig)
    status: StatusConfig = Field(default_factory=StatusConfig)
    trainer: TrainerConfig = Field(default_factory=TrainerConfig)
    evolution: EvolutionConfig = Field(default_factory=EvolutionConfig)
    retreat: RetreatConfig = Field(default_factory=RetreatConfig)
    combat: CombatConfig = Field(default_factory=CombatConfig)

    @classmethod
    def standard_pokemon_tcg(cls) -> "RulesConfig":
        """Create a configuration approximating standard Pokemon TCG rules.

        Returns:
            RulesConfig with settings closer to official Pokemon TCG.
        """
        return cls(
            deck=DeckConfig(
                min_size=60,
                max_size=60,
                energy_deck_enabled=False,
            ),
            prizes=PrizeConfig(
                count=6,
                use_prize_cards=True,
            ),
            first_turn=FirstTurnConfig(
                can_attack=False,
                can_play_supporter=False,
                can_attach_energy=True,
            ),
        )
