"""Tests for the turn manager module.

This module tests:
- Phase transition validation
- Turn start processing (draw, energy flip, counter resets)
- Turn end processing (status damage, recovery, turn advancement)
- Between-turn effects (poison, burn, sleep, paralysis)
- Knockout processing and win condition integration
- Turn limit checking

The tests use SeededRandom for deterministic coin flips.
"""

import pytest

from app.core.config import (
    RulesConfig,
)
from app.core.enums import (
    CardType,
    EnergyType,
    GameEndReason,
    PokemonStage,
    PokemonVariant,
    StatusCondition,
    TurnPhase,
)
from app.core.models.card import CardDefinition, CardInstance
from app.core.models.game_state import GameState, PlayerState
from app.core.rng import SeededRandom
from app.core.turn_manager import (
    VALID_TRANSITIONS,
    PhaseTransitionError,
    TurnEndResult,
    TurnManager,
)

# =============================================================================
# Fixtures
# =============================================================================


@pytest.fixture
def basic_pokemon_def() -> CardDefinition:
    """Create a basic Pokemon card definition."""
    return CardDefinition(
        id="pikachu-001",
        name="Pikachu",
        card_type=CardType.POKEMON,
        stage=PokemonStage.BASIC,
        variant=PokemonVariant.NORMAL,
        hp=60,
        pokemon_type=EnergyType.LIGHTNING,
    )


@pytest.fixture
def ex_pokemon_def() -> CardDefinition:
    """Create an EX Pokemon card definition (worth 2 points)."""
    return CardDefinition(
        id="pikachu-ex-001",
        name="Pikachu EX",
        card_type=CardType.POKEMON,
        stage=PokemonStage.BASIC,
        variant=PokemonVariant.EX,
        hp=120,
        pokemon_type=EnergyType.LIGHTNING,
    )


@pytest.fixture
def energy_def() -> CardDefinition:
    """Create a basic energy card definition."""
    return CardDefinition(
        id="lightning-energy-001",
        name="Lightning Energy",
        card_type=CardType.ENERGY,
        energy_type=EnergyType.LIGHTNING,
    )


@pytest.fixture
def two_player_game(basic_pokemon_def: CardDefinition, energy_def: CardDefinition) -> GameState:
    """Create a minimal two-player game state for testing."""
    # Create card instances
    p1_active = CardInstance(instance_id="p1-active", definition_id=basic_pokemon_def.id)
    p1_bench = CardInstance(instance_id="p1-bench", definition_id=basic_pokemon_def.id)
    p1_deck_cards = [
        CardInstance(instance_id=f"p1-deck-{i}", definition_id=basic_pokemon_def.id)
        for i in range(5)
    ]
    p1_energy_deck = [
        CardInstance(instance_id=f"p1-energy-{i}", definition_id=energy_def.id) for i in range(5)
    ]

    p2_active = CardInstance(instance_id="p2-active", definition_id=basic_pokemon_def.id)
    p2_deck_cards = [
        CardInstance(instance_id=f"p2-deck-{i}", definition_id=basic_pokemon_def.id)
        for i in range(5)
    ]

    # Create player states
    p1 = PlayerState(player_id="player1")
    p1.active.add(p1_active)
    p1.bench.add(p1_bench)
    for card in p1_deck_cards:
        p1.deck.add(card)
    for card in p1_energy_deck:
        p1.energy_deck.add(card)

    p2 = PlayerState(player_id="player2")
    p2.active.add(p2_active)
    for card in p2_deck_cards:
        p2.deck.add(card)

    # Create game state
    game = GameState(
        game_id="test-game",
        rules=RulesConfig(),
        card_registry={
            basic_pokemon_def.id: basic_pokemon_def,
            energy_def.id: energy_def,
        },
        players={"player1": p1, "player2": p2},
        current_player_id="player1",
        turn_number=1,
        phase=TurnPhase.SETUP,
        turn_order=["player1", "player2"],
    )

    return game


@pytest.fixture
def turn_manager() -> TurnManager:
    """Create a TurnManager instance."""
    return TurnManager()


@pytest.fixture
def seeded_rng() -> SeededRandom:
    """Create a seeded RNG for deterministic tests."""
    return SeededRandom(seed=42)


# =============================================================================
# Phase Transition Tests
# =============================================================================


class TestPhaseTransitions:
    """Tests for phase transition validation."""

    def test_valid_transitions_defined(self):
        """
        Test that all phases have valid transitions defined.

        Ensures the VALID_TRANSITIONS constant is complete and covers
        all phases in the game.
        """
        for phase in TurnPhase:
            assert phase in VALID_TRANSITIONS, f"Missing transitions for {phase}"

    def test_setup_to_draw_valid(self, turn_manager: TurnManager):
        """
        Test that SETUP -> DRAW is a valid transition.

        This is the game start transition.
        """
        assert turn_manager.can_transition(TurnPhase.SETUP, TurnPhase.DRAW)

    def test_draw_to_main_valid(self, turn_manager: TurnManager):
        """
        Test that DRAW -> MAIN is a valid transition.

        This is the normal flow after drawing a card.
        """
        assert turn_manager.can_transition(TurnPhase.DRAW, TurnPhase.MAIN)

    def test_main_to_attack_valid(self, turn_manager: TurnManager):
        """
        Test that MAIN -> ATTACK is a valid transition.

        This occurs when the player declares an attack.
        """
        assert turn_manager.can_transition(TurnPhase.MAIN, TurnPhase.ATTACK)

    def test_main_to_end_valid(self, turn_manager: TurnManager):
        """
        Test that MAIN -> END is a valid transition.

        This occurs when the player chooses to skip attacking.
        """
        assert turn_manager.can_transition(TurnPhase.MAIN, TurnPhase.END)

    def test_attack_to_end_valid(self, turn_manager: TurnManager):
        """
        Test that ATTACK -> END is a valid transition.

        This occurs after attack resolution.
        """
        assert turn_manager.can_transition(TurnPhase.ATTACK, TurnPhase.END)

    def test_end_to_draw_valid(self, turn_manager: TurnManager):
        """
        Test that END -> DRAW is a valid transition.

        This occurs when advancing to the next player's turn.
        """
        assert turn_manager.can_transition(TurnPhase.END, TurnPhase.DRAW)

    def test_invalid_setup_to_main(self, turn_manager: TurnManager):
        """
        Test that SETUP -> MAIN is invalid.

        Cannot skip the draw phase.
        """
        assert not turn_manager.can_transition(TurnPhase.SETUP, TurnPhase.MAIN)

    def test_invalid_draw_to_attack(self, turn_manager: TurnManager):
        """
        Test that DRAW -> ATTACK is invalid.

        Cannot skip the main phase.
        """
        assert not turn_manager.can_transition(TurnPhase.DRAW, TurnPhase.ATTACK)

    def test_invalid_attack_to_main(self, turn_manager: TurnManager):
        """
        Test that ATTACK -> MAIN is invalid.

        Cannot go backwards in phase order.
        """
        assert not turn_manager.can_transition(TurnPhase.ATTACK, TurnPhase.MAIN)

    def test_get_valid_transitions_from_main(self, turn_manager: TurnManager):
        """
        Test getting valid transitions from MAIN phase.

        MAIN phase can go to ATTACK or END.
        """
        valid = turn_manager.get_valid_transitions(TurnPhase.MAIN)
        assert TurnPhase.ATTACK in valid
        assert TurnPhase.END in valid
        assert len(valid) == 2

    def test_set_phase_valid_transition(
        self, turn_manager: TurnManager, two_player_game: GameState
    ):
        """
        Test that _set_phase works for valid transitions.

        Verifies the phase is changed when transition is valid.
        """
        two_player_game.phase = TurnPhase.MAIN
        turn_manager._set_phase(two_player_game, TurnPhase.ATTACK)
        assert two_player_game.phase == TurnPhase.ATTACK

    def test_set_phase_invalid_transition_raises(
        self, turn_manager: TurnManager, two_player_game: GameState
    ):
        """
        Test that _set_phase raises for invalid transitions.

        Verifies PhaseTransitionError is raised for invalid transitions.
        """
        two_player_game.phase = TurnPhase.DRAW
        with pytest.raises(PhaseTransitionError) as exc_info:
            turn_manager._set_phase(two_player_game, TurnPhase.ATTACK)
        assert "Invalid phase transition" in str(exc_info.value)
        assert "draw -> attack" in str(exc_info.value).lower()


# =============================================================================
# Turn Start Tests
# =============================================================================


class TestTurnStart:
    """Tests for turn start processing."""

    def test_start_turn_resets_counters(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that start_turn resets per-turn counters.

        Ensures energy attachments, supporter plays, etc. are reset.
        """
        player = two_player_game.get_current_player()
        player.energy_attachments_this_turn = 3
        player.supporters_played_this_turn = 1
        player.retreats_this_turn = 2

        two_player_game.phase = TurnPhase.SETUP
        turn_manager.start_turn(two_player_game, seeded_rng)

        assert player.energy_attachments_this_turn == 0
        assert player.supporters_played_this_turn == 0
        assert player.retreats_this_turn == 0

    def test_start_turn_draws_card(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that start_turn draws a card from deck to hand.

        Verifies deck decreases and hand increases by one.
        """
        two_player_game.phase = TurnPhase.SETUP
        two_player_game.turn_number = 2  # Not first turn
        player = two_player_game.get_current_player()

        initial_deck = len(player.deck)
        initial_hand = len(player.hand)

        result = turn_manager.start_turn(two_player_game, seeded_rng)

        assert result.success
        assert result.drew_card
        assert len(player.deck) == initial_deck - 1
        assert len(player.hand) == initial_hand + 1

    def test_start_turn_first_turn_with_draw(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that first turn draws a card when rules allow.

        Default rules have can_draw=True for first turn.
        """
        two_player_game.phase = TurnPhase.SETUP
        two_player_game.turn_number = 1
        two_player_game.first_turn_completed = False
        player = two_player_game.get_current_player()
        initial_hand = len(player.hand)

        result = turn_manager.start_turn(two_player_game, seeded_rng)

        assert result.success
        assert result.drew_card
        assert len(player.hand) == initial_hand + 1

    def test_start_turn_first_turn_no_draw(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that first turn doesn't draw when rules disallow.

        Some variants don't allow drawing on first turn.
        """
        two_player_game.rules.first_turn.can_draw = False
        two_player_game.phase = TurnPhase.SETUP
        two_player_game.turn_number = 1
        two_player_game.first_turn_completed = False
        player = two_player_game.get_current_player()
        initial_hand = len(player.hand)

        result = turn_manager.start_turn(two_player_game, seeded_rng)

        assert result.success
        assert not result.drew_card
        assert len(player.hand) == initial_hand
        assert "First turn - no draw" in result.message

    def test_start_turn_flips_energy(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that start_turn flips energy from energy deck to zone.

        Pokemon Pocket style auto-energy feature.
        """
        two_player_game.phase = TurnPhase.SETUP
        two_player_game.turn_number = 2  # Not first turn
        player = two_player_game.get_current_player()

        initial_energy_deck = len(player.energy_deck)
        initial_energy_zone = len(player.energy_zone)

        result = turn_manager.start_turn(two_player_game, seeded_rng)

        assert result.success
        assert result.energy_flipped
        assert len(player.energy_deck) == initial_energy_deck - 1
        assert len(player.energy_zone) == initial_energy_zone + 1

    def test_start_turn_no_energy_flip_when_disabled(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that energy is not flipped when auto_flip_from_deck is disabled.

        Standard Pokemon TCG doesn't use energy zone.
        """
        two_player_game.rules.energy.auto_flip_from_deck = False
        two_player_game.phase = TurnPhase.SETUP
        two_player_game.turn_number = 2
        player = two_player_game.get_current_player()

        initial_energy_deck = len(player.energy_deck)

        result = turn_manager.start_turn(two_player_game, seeded_rng)

        assert result.success
        assert not result.energy_flipped
        assert len(player.energy_deck) == initial_energy_deck

    def test_start_turn_no_energy_flip_first_turn(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that energy is not flipped on first turn when disallowed.

        Default rules have can_attach_energy=False for first turn.
        """
        two_player_game.phase = TurnPhase.SETUP
        two_player_game.turn_number = 1
        two_player_game.first_turn_completed = False
        player = two_player_game.get_current_player()

        initial_energy_deck = len(player.energy_deck)
        initial_energy_zone = len(player.energy_zone)

        result = turn_manager.start_turn(two_player_game, seeded_rng)

        assert result.success
        assert not result.energy_flipped
        assert len(player.energy_deck) == initial_energy_deck
        assert len(player.energy_zone) == initial_energy_zone

    def test_start_turn_advances_to_main(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that start_turn advances phase to MAIN.

        Verifies the automatic phase progression.
        """
        two_player_game.phase = TurnPhase.SETUP

        turn_manager.start_turn(two_player_game, seeded_rng)

        assert two_player_game.phase == TurnPhase.MAIN

    def test_start_turn_deck_empty_triggers_win(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that start_turn returns win result when deck is empty.

        Cannot draw = opponent wins.
        """
        two_player_game.phase = TurnPhase.SETUP
        player = two_player_game.get_current_player()
        player.deck.clear()

        result = turn_manager.start_turn(two_player_game, seeded_rng)

        assert not result.success
        assert result.win_result is not None
        assert result.win_result.winner_id == "player2"
        assert result.win_result.end_reason == GameEndReason.DECK_EMPTY

    def test_start_turn_deck_empty_check_disabled(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that deck empty doesn't trigger win when condition disabled.

        Some game variants may not use deck-out as a win condition.
        """
        two_player_game.rules.win_conditions.cannot_draw = False
        two_player_game.phase = TurnPhase.SETUP
        player = two_player_game.get_current_player()
        player.deck.clear()

        result = turn_manager.start_turn(two_player_game, seeded_rng)

        assert result.success
        assert result.win_result is None
        assert not result.drew_card


# =============================================================================
# Phase Advancement Tests
# =============================================================================


class TestPhaseAdvancement:
    """Tests for explicit phase advancement methods."""

    def test_advance_to_main(self, turn_manager: TurnManager, two_player_game: GameState):
        """
        Test advance_to_main from DRAW phase.

        Verifies explicit phase control.
        """
        two_player_game.phase = TurnPhase.DRAW
        turn_manager.advance_to_main(two_player_game)
        assert two_player_game.phase == TurnPhase.MAIN

    def test_advance_to_main_invalid_phase(
        self, turn_manager: TurnManager, two_player_game: GameState
    ):
        """
        Test advance_to_main from invalid phase raises error.

        Cannot go to MAIN from ATTACK.
        """
        two_player_game.phase = TurnPhase.ATTACK
        with pytest.raises(PhaseTransitionError):
            turn_manager.advance_to_main(two_player_game)

    def test_advance_to_attack(self, turn_manager: TurnManager, two_player_game: GameState):
        """
        Test advance_to_attack from MAIN phase.

        Standard attack declaration flow.
        """
        two_player_game.phase = TurnPhase.MAIN
        turn_manager.advance_to_attack(two_player_game)
        assert two_player_game.phase == TurnPhase.ATTACK

    def test_advance_to_attack_invalid_phase(
        self, turn_manager: TurnManager, two_player_game: GameState
    ):
        """
        Test advance_to_attack from invalid phase raises error.

        Cannot attack directly from DRAW.
        """
        two_player_game.phase = TurnPhase.DRAW
        with pytest.raises(PhaseTransitionError):
            turn_manager.advance_to_attack(two_player_game)

    def test_advance_to_end_from_main(self, turn_manager: TurnManager, two_player_game: GameState):
        """
        Test advance_to_end from MAIN phase (skip attack).

        Player chooses not to attack.
        """
        two_player_game.phase = TurnPhase.MAIN
        turn_manager.advance_to_end(two_player_game)
        assert two_player_game.phase == TurnPhase.END

    def test_advance_to_end_from_attack(
        self, turn_manager: TurnManager, two_player_game: GameState
    ):
        """
        Test advance_to_end from ATTACK phase.

        After attack resolution.
        """
        two_player_game.phase = TurnPhase.ATTACK
        turn_manager.advance_to_end(two_player_game)
        assert two_player_game.phase == TurnPhase.END

    def test_skip_attack(self, turn_manager: TurnManager, two_player_game: GameState):
        """
        Test skip_attack from MAIN phase.

        Convenience method for skipping attack.
        """
        two_player_game.phase = TurnPhase.MAIN
        turn_manager.skip_attack(two_player_game)
        assert two_player_game.phase == TurnPhase.END

    def test_skip_attack_wrong_phase(self, turn_manager: TurnManager, two_player_game: GameState):
        """
        Test skip_attack from wrong phase raises error.

        Can only skip attack from MAIN.
        """
        two_player_game.phase = TurnPhase.ATTACK
        with pytest.raises(PhaseTransitionError) as exc_info:
            turn_manager.skip_attack(two_player_game)
        assert "Can only skip attack from MAIN phase" in str(exc_info.value)


# =============================================================================
# Turn End Tests
# =============================================================================


class TestTurnEnd:
    """Tests for turn end processing."""

    def test_end_turn_advances_to_next_player(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that end_turn advances to the next player.

        Verifies turn order cycling.
        """
        two_player_game.phase = TurnPhase.END
        two_player_game.turn_number = 1

        turn_manager.end_turn(two_player_game, seeded_rng)

        assert two_player_game.current_player_id == "player2"
        assert two_player_game.phase == TurnPhase.DRAW

    def test_end_turn_from_main_phase(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that end_turn can be called from MAIN phase.

        Automatically advances to END first.
        """
        two_player_game.phase = TurnPhase.MAIN

        result = turn_manager.end_turn(two_player_game, seeded_rng)

        assert result.success
        assert two_player_game.current_player_id == "player2"

    def test_end_turn_from_attack_phase(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that end_turn can be called from ATTACK phase.

        Automatically advances to END first.
        """
        two_player_game.phase = TurnPhase.ATTACK

        result = turn_manager.end_turn(two_player_game, seeded_rng)

        assert result.success
        assert two_player_game.current_player_id == "player2"

    def test_end_turn_returns_result(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that end_turn returns a proper result object.

        Verifies the TurnEndResult structure.
        """
        two_player_game.phase = TurnPhase.END

        result = turn_manager.end_turn(two_player_game, seeded_rng)

        assert isinstance(result, TurnEndResult)
        assert result.success


# =============================================================================
# Between-Turn Effect Tests (Status Conditions)
# =============================================================================


class TestBetweenTurnEffects:
    """Tests for between-turn status effect processing."""

    def test_poison_damage_applied(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that poison damage is applied at end of turn.

        Default poison damage is 10.
        """
        two_player_game.phase = TurnPhase.END
        active = two_player_game.get_current_player().get_active_pokemon()
        active.add_status(StatusCondition.POISONED)
        initial_damage = active.damage

        result = turn_manager.end_turn(two_player_game, seeded_rng)

        assert result.success
        assert active.instance_id in result.between_turn_damage
        assert result.between_turn_damage[active.instance_id] == 10
        assert active.damage == initial_damage + 10

    def test_poison_damage_custom_amount(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that poison damage uses configured amount.

        Verifies StatusConfig.poison_damage is respected.
        """
        two_player_game.rules.status.poison_damage = 20
        two_player_game.phase = TurnPhase.END
        active = two_player_game.get_current_player().get_active_pokemon()
        active.add_status(StatusCondition.POISONED)

        result = turn_manager.end_turn(two_player_game, seeded_rng)

        assert result.between_turn_damage[active.instance_id] == 20

    def test_burn_damage_applied(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that burn damage is applied at end of turn.

        Default burn damage is 20.
        """
        two_player_game.phase = TurnPhase.END
        active = two_player_game.get_current_player().get_active_pokemon()
        active.add_status(StatusCondition.BURNED)
        initial_damage = active.damage

        result = turn_manager.end_turn(two_player_game, seeded_rng)

        assert active.instance_id in result.between_turn_damage
        assert result.between_turn_damage[active.instance_id] == 20
        assert active.damage == initial_damage + 20

    def test_burn_damage_custom_amount(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that burn damage uses configured amount.

        Verifies StatusConfig.burn_damage is respected.
        """
        two_player_game.rules.status.burn_damage = 30
        two_player_game.phase = TurnPhase.END
        active = two_player_game.get_current_player().get_active_pokemon()
        active.add_status(StatusCondition.BURNED)

        result = turn_manager.end_turn(two_player_game, seeded_rng)

        assert result.between_turn_damage[active.instance_id] == 30

    def test_poison_and_burn_stack(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that poison and burn damage stack.

        Both conditions can be active simultaneously.
        """
        two_player_game.phase = TurnPhase.END
        active = two_player_game.get_current_player().get_active_pokemon()
        active.add_status(StatusCondition.POISONED)
        active.add_status(StatusCondition.BURNED)
        initial_damage = active.damage

        result = turn_manager.end_turn(two_player_game, seeded_rng)

        # 10 poison + 20 burn = 30 total
        assert result.between_turn_damage[active.instance_id] == 30
        assert active.damage == initial_damage + 30

    def test_burn_recovery_flip_heads(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
    ):
        """
        Test that burn can be removed on heads.

        Uses seeded RNG to get consistent heads.
        """
        # Seed 12345 gives heads on first flip
        rng = SeededRandom(seed=12345)
        two_player_game.phase = TurnPhase.END
        active = two_player_game.get_current_player().get_active_pokemon()
        active.add_status(StatusCondition.BURNED)

        result = turn_manager.end_turn(two_player_game, rng)

        assert StatusCondition.BURNED not in active.status_conditions
        assert active.instance_id in result.status_removed
        assert StatusCondition.BURNED in result.status_removed[active.instance_id]

    def test_burn_remains_on_tails(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
    ):
        """
        Test that burn remains on tails.

        Uses seeded RNG to get consistent tails.
        """
        # Seed 42 gives tails on first flip
        rng = SeededRandom(seed=42)
        two_player_game.phase = TurnPhase.END
        active = two_player_game.get_current_player().get_active_pokemon()
        active.add_status(StatusCondition.BURNED)

        turn_manager.end_turn(two_player_game, rng)

        assert StatusCondition.BURNED in active.status_conditions

    def test_sleep_recovery_flip_heads(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
    ):
        """
        Test that sleep can be removed on heads.

        Uses seeded RNG to get consistent heads.
        """
        # Seed 12345 gives heads on first flip
        rng = SeededRandom(seed=12345)
        two_player_game.phase = TurnPhase.END
        active = two_player_game.get_current_player().get_active_pokemon()
        active.add_status(StatusCondition.ASLEEP)

        result = turn_manager.end_turn(two_player_game, rng)

        assert StatusCondition.ASLEEP not in active.status_conditions
        assert active.instance_id in result.status_removed
        assert StatusCondition.ASLEEP in result.status_removed[active.instance_id]

    def test_sleep_remains_on_tails(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
    ):
        """
        Test that sleep remains on tails.

        Uses seeded RNG to get consistent tails.
        """
        # Seed 42 gives tails on first flip
        rng = SeededRandom(seed=42)
        two_player_game.phase = TurnPhase.END
        active = two_player_game.get_current_player().get_active_pokemon()
        active.add_status(StatusCondition.ASLEEP)

        turn_manager.end_turn(two_player_game, rng)

        assert StatusCondition.ASLEEP in active.status_conditions

    def test_paralysis_removed_end_of_turn(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that paralysis is always removed at end of turn.

        Paralysis only lasts one turn.
        """
        two_player_game.phase = TurnPhase.END
        active = two_player_game.get_current_player().get_active_pokemon()
        active.add_status(StatusCondition.PARALYZED)

        result = turn_manager.end_turn(two_player_game, seeded_rng)

        assert StatusCondition.PARALYZED not in active.status_conditions
        assert active.instance_id in result.status_removed
        assert StatusCondition.PARALYZED in result.status_removed[active.instance_id]

    def test_confusion_persists(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that confusion is not removed automatically at end of turn.

        Confusion is removed by retreat or evolution, not by time.
        """
        two_player_game.phase = TurnPhase.END
        active = two_player_game.get_current_player().get_active_pokemon()
        active.add_status(StatusCondition.CONFUSED)

        result = turn_manager.end_turn(two_player_game, seeded_rng)

        # Confusion should still be active
        assert StatusCondition.CONFUSED in active.status_conditions
        # Should not be in status_removed
        assert active.instance_id not in result.status_removed or (
            StatusCondition.CONFUSED not in result.status_removed.get(active.instance_id, [])
        )

    def test_status_damage_causes_knockout(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
        basic_pokemon_def: CardDefinition,
    ):
        """
        Test that status damage can knockout a Pokemon.

        If damage exceeds HP, the Pokemon is knocked out.
        """
        two_player_game.phase = TurnPhase.END
        active = two_player_game.get_current_player().get_active_pokemon()
        # Set damage to 50, HP is 60, poison will deal 10 for KO
        active.damage = 50
        active.add_status(StatusCondition.POISONED)

        result = turn_manager.end_turn(two_player_game, seeded_rng)

        assert active.instance_id in result.knockouts


# =============================================================================
# Knockout Processing Tests
# =============================================================================


class TestKnockoutProcessing:
    """Tests for knockout processing and win condition integration."""

    def test_process_knockout_moves_to_discard(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
    ):
        """
        Test that knocked out Pokemon is moved to discard pile.

        Standard knockout handling.
        """
        active = two_player_game.players["player1"].get_active_pokemon()
        active_id = active.instance_id

        turn_manager.process_knockout(two_player_game, active_id, "player2")

        assert len(two_player_game.players["player1"].active) == 0
        assert active_id in two_player_game.players["player1"].discard

    def test_process_knockout_awards_points(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
    ):
        """
        Test that opponent is awarded points for knockout.

        Normal Pokemon = 1 point.
        """
        active = two_player_game.players["player1"].get_active_pokemon()
        active_id = active.instance_id
        initial_score = two_player_game.players["player2"].score

        turn_manager.process_knockout(two_player_game, active_id, "player2")

        assert two_player_game.players["player2"].score == initial_score + 1

    def test_process_knockout_ex_awards_two_points(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        ex_pokemon_def: CardDefinition,
    ):
        """
        Test that EX Pokemon knockout awards 2 points.

        EX, V, GX variants are worth 2 points.
        """
        # Replace active with EX Pokemon
        player1 = two_player_game.players["player1"]
        player1.active.clear()
        ex_card = CardInstance(instance_id="ex-active", definition_id=ex_pokemon_def.id)
        player1.active.add(ex_card)
        two_player_game.card_registry[ex_pokemon_def.id] = ex_pokemon_def

        initial_score = two_player_game.players["player2"].score

        turn_manager.process_knockout(two_player_game, "ex-active", "player2")

        assert two_player_game.players["player2"].score == initial_score + 2

    def test_process_knockout_triggers_win_by_points(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
    ):
        """
        Test that knockout triggers win when points reach target.

        Default is 4 points to win.
        """
        two_player_game.players["player2"].score = 3  # Need 1 more to win
        active = two_player_game.players["player1"].get_active_pokemon()
        active_id = active.instance_id

        result = turn_manager.process_knockout(two_player_game, active_id, "player2")

        assert result is not None
        assert result.winner_id == "player2"
        assert result.end_reason == GameEndReason.PRIZES_TAKEN

    def test_process_knockout_triggers_win_by_no_pokemon(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
    ):
        """
        Test that knockout triggers win when player has no Pokemon.

        If last Pokemon is knocked out, opponent wins.
        """
        # Remove bench Pokemon first
        two_player_game.players["player1"].bench.clear()
        active = two_player_game.players["player1"].get_active_pokemon()
        active_id = active.instance_id

        result = turn_manager.process_knockout(two_player_game, active_id, "player2")

        assert result is not None
        assert result.winner_id == "player2"
        assert result.end_reason == GameEndReason.NO_POKEMON

    def test_process_knockout_sets_forced_action(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
    ):
        """
        Test that knockout sets forced action to select new active.

        When active is KO'd but bench has Pokemon, player must select new active.
        """
        active = two_player_game.players["player1"].get_active_pokemon()
        active_id = active.instance_id

        turn_manager.process_knockout(two_player_game, active_id, "player2")

        current_forced = two_player_game.get_current_forced_action()
        assert current_forced is not None
        assert current_forced.player_id == "player1"
        assert current_forced.action_type == "select_active"

    def test_process_bench_knockout(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
    ):
        """
        Test that bench Pokemon can be knocked out.

        For effects like bench damage.
        """
        bench_card = two_player_game.players["player1"].bench.cards[0]
        bench_id = bench_card.instance_id
        initial_score = two_player_game.players["player2"].score

        turn_manager.process_knockout(two_player_game, bench_id, "player2")

        assert len(two_player_game.players["player1"].bench) == 0
        assert bench_id in two_player_game.players["player1"].discard
        assert two_player_game.players["player2"].score == initial_score + 1


# =============================================================================
# Status Knockout Integration Tests (Issues #3, #4, #6 verification)
# =============================================================================


class TestStatusKnockoutIntegration:
    """Integration tests verifying full end_turn knockout processing.

    These tests verify that Issues #3, #4, and #6 from SYSTEM_REVIEW.md are fixed:
    - Issue #3: end_turn() processes knockouts (moves to discard, awards points)
    - Issue #4: Win conditions are checked AFTER knockout processing
    - Issue #6: The full knockout flow works end-to-end
    """

    def test_end_turn_poison_knockout_moves_to_discard(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that poison knockout moves Pokemon to discard during end_turn.

        Verifies Issue #3: Pokemon knocked out by status damage should be
        properly moved from active zone to discard pile, not just added to
        the knockouts list.
        """
        two_player_game.phase = TurnPhase.END
        player = two_player_game.get_current_player()
        active = player.get_active_pokemon()
        active_id = active.instance_id

        # Set up for lethal poison damage (60 HP, 50 damage, poison deals 10)
        active.damage = 50
        active.add_status(StatusCondition.POISONED)

        result = turn_manager.end_turn(two_player_game, seeded_rng)

        # Verify knockout was detected
        assert active_id in result.knockouts
        # Verify Pokemon was moved to discard (Issue #3 fix)
        assert active_id in player.discard
        assert len(player.active) == 0

    def test_end_turn_burn_knockout_moves_to_discard(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that burn knockout moves Pokemon to discard during end_turn.

        Same as poison test but with burn damage (20 instead of 10).
        """
        two_player_game.phase = TurnPhase.END
        player = two_player_game.get_current_player()
        active = player.get_active_pokemon()
        active_id = active.instance_id

        # Set up for lethal burn damage (60 HP, 40 damage, burn deals 20)
        active.damage = 40
        active.add_status(StatusCondition.BURNED)

        result = turn_manager.end_turn(two_player_game, seeded_rng)

        assert active_id in result.knockouts
        assert active_id in player.discard
        assert len(player.active) == 0

    def test_end_turn_status_knockout_awards_points(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that status knockout awards points to the opponent.

        Verifies Issue #3: Opponent should receive points for status KOs,
        just like attack KOs.
        """
        two_player_game.phase = TurnPhase.END
        player = two_player_game.get_current_player()
        opponent = two_player_game.players["player2"]
        active = player.get_active_pokemon()

        initial_score = opponent.score
        active.damage = 50
        active.add_status(StatusCondition.POISONED)

        turn_manager.end_turn(two_player_game, seeded_rng)

        # Normal Pokemon is worth 1 point
        assert opponent.score == initial_score + 1

    def test_end_turn_status_knockout_ex_awards_two_points(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
        ex_pokemon_def: CardDefinition,
    ):
        """
        Test that status knockout of EX Pokemon awards 2 points.

        EX/GX Pokemon are worth 2 prize points when knocked out.
        """
        # Replace active with EX Pokemon
        two_player_game.card_registry[ex_pokemon_def.id] = ex_pokemon_def
        player = two_player_game.get_current_player()
        player.active.cards.clear()
        ex_pokemon = CardInstance(instance_id="ex-active", definition_id=ex_pokemon_def.id)
        player.active.add(ex_pokemon)

        two_player_game.phase = TurnPhase.END
        opponent = two_player_game.players["player2"]
        initial_score = opponent.score

        # EX has 120 HP
        ex_pokemon.damage = 110
        ex_pokemon.add_status(StatusCondition.POISONED)

        turn_manager.end_turn(two_player_game, seeded_rng)

        # EX Pokemon is worth 2 points
        assert opponent.score == initial_score + 2

    def test_end_turn_status_knockout_discards_attached_energy(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
        energy_def: CardDefinition,
    ):
        """
        Test that attached energy is discarded when Pokemon is knocked out by status.

        Verifies that the full knockout processing (including attachments)
        happens during end_turn, not just detecting the KO.
        """
        two_player_game.phase = TurnPhase.END
        player = two_player_game.get_current_player()
        active = player.get_active_pokemon()

        # Attach energy to the Pokemon
        energy1 = CardInstance(instance_id="energy-1", definition_id=energy_def.id)
        energy2 = CardInstance(instance_id="energy-2", definition_id=energy_def.id)
        active.attached_energy = [energy1, energy2]

        active.damage = 50
        active.add_status(StatusCondition.POISONED)

        turn_manager.end_turn(two_player_game, seeded_rng)

        # Both energy cards should be in discard
        assert "energy-1" in player.discard
        assert "energy-2" in player.discard

    def test_end_turn_status_knockout_triggers_win_by_points(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
        ex_pokemon_def: CardDefinition,
    ):
        """
        Test that status knockout can trigger win by points.

        Verifies Issue #4: Win condition check happens AFTER knockout
        processing, so the points are correctly counted.
        """
        # Set opponent to 5 points (need 6 to win with default rules)
        two_player_game.card_registry[ex_pokemon_def.id] = ex_pokemon_def
        player = two_player_game.get_current_player()
        opponent = two_player_game.players["player2"]
        opponent.score = 5

        # Replace active with EX Pokemon (worth 2 points)
        player.active.cards.clear()
        ex_pokemon = CardInstance(instance_id="ex-active", definition_id=ex_pokemon_def.id)
        player.active.add(ex_pokemon)

        two_player_game.phase = TurnPhase.END
        ex_pokemon.damage = 110
        ex_pokemon.add_status(StatusCondition.POISONED)

        result = turn_manager.end_turn(two_player_game, seeded_rng)

        # Opponent should win (5 + 2 = 7 >= 6)
        assert result.win_result is not None
        assert result.win_result.winner_id == "player2"
        assert result.win_result.end_reason == GameEndReason.PRIZES_TAKEN

    def test_end_turn_status_knockout_triggers_win_by_no_pokemon(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that status knockout can trigger win by no Pokemon in play.

        Verifies Issue #4: Win condition check for "no Pokemon in play"
        happens AFTER the Pokemon is actually removed from play.
        """
        two_player_game.phase = TurnPhase.END
        player = two_player_game.get_current_player()
        active = player.get_active_pokemon()

        # Clear bench so player has only active Pokemon
        player.bench.cards.clear()

        active.damage = 50
        active.add_status(StatusCondition.POISONED)

        result = turn_manager.end_turn(two_player_game, seeded_rng)

        # Opponent should win because player has no Pokemon left
        assert result.win_result is not None
        assert result.win_result.winner_id == "player2"
        assert result.win_result.loser_id == "player1"
        assert result.win_result.end_reason == GameEndReason.NO_POKEMON

    def test_end_turn_status_knockout_with_bench_sets_forced_action(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that status knockout sets forced action when player has bench Pokemon.

        If the knocked out Pokemon was active but player has bench Pokemon,
        they must select a new active Pokemon.
        """
        two_player_game.phase = TurnPhase.END
        player = two_player_game.get_current_player()
        active = player.get_active_pokemon()

        # Ensure player has bench Pokemon
        assert len(player.bench) > 0

        active.damage = 50
        active.add_status(StatusCondition.POISONED)

        turn_manager.end_turn(two_player_game, seeded_rng)

        # Should have forced action to select new active
        current_forced = two_player_game.get_current_forced_action()
        assert current_forced is not None
        assert current_forced.player_id == player.player_id
        assert current_forced.action_type == "select_active"


# =============================================================================
# Prize Card Mode Tests (Issue #11)
# =============================================================================


class TestPrizeCardModeKnockout:
    """Tests for knockout processing in prize card mode.

    These tests verify that process_knockout handles prize card mode correctly,
    including random and player-choice selection.
    """

    @pytest.fixture
    def prize_card_game(
        self,
        basic_pokemon_def: CardDefinition,
        energy_def: CardDefinition,
    ) -> GameState:
        """Create a game state with prize card mode enabled."""
        from app.core.config import PrizeConfig

        # Create card registry
        card_registry = {
            basic_pokemon_def.id: basic_pokemon_def,
            energy_def.id: energy_def,
        }

        # Create player states
        p1_active = CardInstance(instance_id="p1-active", definition_id=basic_pokemon_def.id)
        p1_bench = CardInstance(instance_id="p1-bench", definition_id=basic_pokemon_def.id)
        p2_active = CardInstance(instance_id="p2-active", definition_id=basic_pokemon_def.id)

        p1 = PlayerState(player_id="player1")
        p1.active.add(p1_active)
        p1.bench.add(p1_bench)
        # Add prize cards
        for i in range(6):
            p1.prizes.add(
                CardInstance(instance_id=f"p1-prize-{i}", definition_id=basic_pokemon_def.id)
            )

        p2 = PlayerState(player_id="player2")
        p2.active.add(p2_active)
        # Add prize cards
        for i in range(6):
            p2.prizes.add(
                CardInstance(instance_id=f"p2-prize-{i}", definition_id=basic_pokemon_def.id)
            )

        # Create game state with prize card mode
        rules = RulesConfig()
        rules.prizes = PrizeConfig(
            count=6,
            use_prize_cards=True,
            prize_selection_random=True,  # Random by default
        )

        game = GameState(
            game_id="test-prize",
            rules=rules,
            card_registry=card_registry,
            players={"player1": p1, "player2": p2},
            turn_order=["player1", "player2"],
            active_player_index=0,
            current_player_id="player1",
            turn_number=1,
            phase=TurnPhase.MAIN,
        )

        return game

    def test_knockout_random_prize_selection(
        self,
        turn_manager: TurnManager,
        prize_card_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that knockout with random prize selection auto-takes prize.

        In random mode, prize cards are taken automatically.
        """
        opponent = prize_card_game.players["player2"]
        initial_hand = len(opponent.hand)
        initial_prizes = len(opponent.prizes)

        # Process knockout - opponent (player2) takes prize from player1's knockout
        turn_manager.process_knockout(prize_card_game, "p1-active", "player2", seeded_rng)

        # Opponent should have taken 1 prize card
        assert len(opponent.hand) == initial_hand + 1
        assert len(opponent.prizes) == initial_prizes - 1

    def test_knockout_player_choice_sets_forced_action(
        self,
        turn_manager: TurnManager,
        prize_card_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that knockout with player choice sets forced action.

        In player choice mode, a forced action is set for prize selection.
        Note: When the knocked out player also needs to select a new active,
        select_active takes priority. This test uses a bench knockout to
        avoid that complication.
        """
        # Switch to player choice mode
        prize_card_game.rules.prizes.prize_selection_random = False

        # Use bench knockout to avoid select_active conflict
        player = prize_card_game.players["player1"]
        bench_id = player.bench.cards[0].instance_id

        turn_manager.process_knockout(prize_card_game, bench_id, "player2", seeded_rng)

        # Should have forced action for prize selection
        current_forced = prize_card_game.get_current_forced_action()
        assert current_forced is not None
        assert current_forced.action_type == "select_prize"
        assert current_forced.player_id == "player2"
        assert current_forced.params.get("count") == 1

    def test_knockout_ex_awards_two_prizes(
        self,
        turn_manager: TurnManager,
        prize_card_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that knockout of EX Pokemon awards 2 prizes.

        EX/GX Pokemon are worth 2 prize cards.
        """
        from app.core.enums import PokemonVariant

        # Add EX pokemon definition
        ex_def = CardDefinition(
            id="ex-pokemon",
            name="Pikachu EX",
            card_type=CardType.POKEMON,
            stage=PokemonStage.BASIC,
            variant=PokemonVariant.EX,
            hp=120,
            pokemon_type=EnergyType.LIGHTNING,
        )
        prize_card_game.card_registry[ex_def.id] = ex_def

        # Replace active with EX
        player = prize_card_game.players["player1"]
        player.active.cards.clear()
        ex_pokemon = CardInstance(instance_id="p1-ex", definition_id=ex_def.id)
        player.active.add(ex_pokemon)

        opponent = prize_card_game.players["player2"]
        initial_hand = len(opponent.hand)

        turn_manager.process_knockout(prize_card_game, "p1-ex", "player2", seeded_rng)

        # Opponent should have taken 2 prize cards
        assert len(opponent.hand) == initial_hand + 2

    def test_knockout_win_by_empty_prizes(
        self,
        turn_manager: TurnManager,
        prize_card_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test that taking all prizes triggers a win.

        Win condition when opponent's prize pile is empty.
        """
        opponent = prize_card_game.players["player2"]

        # Remove all but one prize
        while len(opponent.prizes) > 1:
            opponent.prizes.cards.pop()

        result = turn_manager.process_knockout(prize_card_game, "p1-active", "player2", seeded_rng)

        # Should win by taking all prizes
        assert result is not None
        assert result.winner_id == "player2"
        assert result.end_reason == GameEndReason.PRIZES_TAKEN


# =============================================================================
# Turn Limit Tests
# =============================================================================


class TestTurnLimit:
    """Tests for turn limit checking."""

    def test_check_turn_limit_not_reached(
        self, turn_manager: TurnManager, two_player_game: GameState
    ):
        """
        Test that no win result when turn limit not reached.

        Game continues normally.
        """
        two_player_game.turn_number = 15  # Default limit is 30

        result = turn_manager.check_turn_limit(two_player_game)

        assert result is None

    def test_check_turn_limit_reached_higher_score_wins(
        self, turn_manager: TurnManager, two_player_game: GameState
    ):
        """
        Test that higher score wins when turn limit reached.

        Standard turn limit resolution.
        """
        two_player_game.rules.win_conditions.turn_limit = 30
        two_player_game.turn_number = 31  # Over limit
        two_player_game.players["player1"].score = 3
        two_player_game.players["player2"].score = 2

        result = turn_manager.check_turn_limit(two_player_game)

        assert result is not None
        assert result.winner_id == "player1"

    def test_check_turn_limit_disabled(self, turn_manager: TurnManager, two_player_game: GameState):
        """
        Test that turn limit check returns None when disabled.

        Some game modes may not have turn limits.
        """
        two_player_game.rules.win_conditions.turn_limit_enabled = False
        two_player_game.turn_number = 100  # Way over default limit

        result = turn_manager.check_turn_limit(two_player_game)

        assert result is None


# =============================================================================
# Integration Tests
# =============================================================================


class TestTurnManagerIntegration:
    """Integration tests for complete turn flows."""

    def test_complete_turn_cycle(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test a complete turn cycle: start -> main -> attack -> end.

        Verifies full turn flow works correctly.
        """
        # Start turn (player 1)
        result = turn_manager.start_turn(two_player_game, seeded_rng)
        assert result.success
        assert two_player_game.phase == TurnPhase.MAIN
        assert two_player_game.current_player_id == "player1"

        # Advance to attack
        turn_manager.advance_to_attack(two_player_game)
        assert two_player_game.phase == TurnPhase.ATTACK

        # End turn
        result = turn_manager.end_turn(two_player_game, seeded_rng)
        assert result.success
        assert two_player_game.current_player_id == "player2"
        assert two_player_game.phase == TurnPhase.DRAW

    def test_skip_attack_turn_cycle(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test turn cycle with skipped attack.

        Player can choose not to attack.
        """
        # Start turn
        turn_manager.start_turn(two_player_game, seeded_rng)
        assert two_player_game.phase == TurnPhase.MAIN

        # Skip attack and end turn
        turn_manager.skip_attack(two_player_game)
        assert two_player_game.phase == TurnPhase.END

        result = turn_manager.end_turn(two_player_game, seeded_rng)
        assert result.success
        assert two_player_game.current_player_id == "player2"

    def test_multiple_turns(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test multiple turn cycles.

        Verifies turn order and state transitions over multiple turns.
        """
        # Turn 1 - Player 1
        turn_manager.start_turn(two_player_game, seeded_rng)
        turn_manager.end_turn(two_player_game, seeded_rng)
        assert two_player_game.current_player_id == "player2"

        # Turn 1 - Player 2
        turn_manager.start_turn(two_player_game, seeded_rng)
        turn_manager.end_turn(two_player_game, seeded_rng)
        assert two_player_game.current_player_id == "player1"
        assert two_player_game.turn_number == 2

        # Turn 2 - Player 1
        turn_manager.start_turn(two_player_game, seeded_rng)
        turn_manager.end_turn(two_player_game, seeded_rng)
        assert two_player_game.current_player_id == "player2"

    def test_turn_with_status_damage(
        self,
        turn_manager: TurnManager,
        two_player_game: GameState,
        seeded_rng: SeededRandom,
    ):
        """
        Test turn cycle with status damage applied.

        Verifies status effects are processed at end of turn.
        """
        # Start turn
        turn_manager.start_turn(two_player_game, seeded_rng)

        # Apply poison to active
        active = two_player_game.get_current_player().get_active_pokemon()
        active.add_status(StatusCondition.POISONED)
        initial_damage = active.damage

        # End turn - poison should deal damage
        result = turn_manager.end_turn(two_player_game, seeded_rng)

        assert result.success
        assert active.damage == initial_damage + 10
        assert active.instance_id in result.between_turn_damage
