"""
Unit tests for PlayOutcome enum.

Tests helper methods and outcome categorization.
"""
import pytest
from app.config import PlayOutcome


class TestPlayOutcomeHelpers:
    """Tests for PlayOutcome helper methods."""

    def test_is_hit_for_singles(self):
        """Single outcomes are hits."""
        assert PlayOutcome.SINGLE_1.is_hit() is True
        assert PlayOutcome.SINGLE_2.is_hit() is True
        assert PlayOutcome.SINGLE_UNCAPPED.is_hit() is True
        assert PlayOutcome.BP_SINGLE.is_hit() is True

    def test_is_hit_for_extra_bases(self):
        """Extra base hits are hits."""
        assert PlayOutcome.DOUBLE_2.is_hit() is True
        assert PlayOutcome.DOUBLE_3.is_hit() is True
        assert PlayOutcome.DOUBLE_UNCAPPED.is_hit() is True
        assert PlayOutcome.TRIPLE.is_hit() is True
        assert PlayOutcome.HOMERUN.is_hit() is True
        assert PlayOutcome.BP_HOMERUN.is_hit() is True

    def test_is_hit_false_for_outs(self):
        """Outs are not hits."""
        assert PlayOutcome.STRIKEOUT.is_hit() is False
        assert PlayOutcome.GROUNDBALL_A.is_hit() is False
        assert PlayOutcome.FLYOUT_A.is_hit() is False
        assert PlayOutcome.BP_FLYOUT.is_hit() is False

    def test_is_hit_false_for_walks(self):
        """Walks are not hits."""
        assert PlayOutcome.WALK.is_hit() is False
        assert PlayOutcome.INTENTIONAL_WALK.is_hit() is False

    def test_is_out_for_standard_outs(self):
        """Standard outs are outs."""
        assert PlayOutcome.STRIKEOUT.is_out() is True
        assert PlayOutcome.GROUNDBALL_A.is_out() is True
        assert PlayOutcome.GROUNDBALL_B.is_out() is True
        assert PlayOutcome.GROUNDBALL_C.is_out() is True
        assert PlayOutcome.FLYOUT_A.is_out() is True
        assert PlayOutcome.FLYOUT_B.is_out() is True
        assert PlayOutcome.FLYOUT_C.is_out() is True
        assert PlayOutcome.LINEOUT.is_out() is True
        assert PlayOutcome.POPOUT.is_out() is True

    def test_is_out_for_baserunning_outs(self):
        """Baserunning outs are outs."""
        assert PlayOutcome.CAUGHT_STEALING.is_out() is True
        assert PlayOutcome.PICK_OFF.is_out() is True

    def test_is_out_for_ballpark_outs(self):
        """Ballpark outs are outs."""
        assert PlayOutcome.BP_FLYOUT.is_out() is True
        assert PlayOutcome.BP_LINEOUT.is_out() is True

    def test_is_out_false_for_hits(self):
        """Hits are not outs."""
        assert PlayOutcome.SINGLE_1.is_out() is False
        assert PlayOutcome.HOMERUN.is_out() is False

    def test_is_walk(self):
        """Walk outcomes are walks."""
        assert PlayOutcome.WALK.is_walk() is True
        assert PlayOutcome.INTENTIONAL_WALK.is_walk() is True

    def test_is_walk_false_for_hits(self):
        """Hits are not walks."""
        assert PlayOutcome.SINGLE_1.is_walk() is False
        assert PlayOutcome.HOMERUN.is_walk() is False

    def test_is_walk_false_for_hbp(self):
        """HBP is not a walk (different stat)."""
        assert PlayOutcome.HIT_BY_PITCH.is_walk() is False

    def test_is_uncapped(self):
        """Uncapped outcomes are uncapped."""
        assert PlayOutcome.SINGLE_UNCAPPED.is_uncapped() is True
        assert PlayOutcome.DOUBLE_UNCAPPED.is_uncapped() is True

    def test_is_uncapped_false_for_normal_hits(self):
        """Normal hits are not uncapped."""
        assert PlayOutcome.SINGLE_1.is_uncapped() is False
        assert PlayOutcome.DOUBLE_2.is_uncapped() is False
        assert PlayOutcome.TRIPLE.is_uncapped() is False

    def test_is_interrupt(self):
        """Interrupt plays are interrupts."""
        assert PlayOutcome.WILD_PITCH.is_interrupt() is True
        assert PlayOutcome.PASSED_BALL.is_interrupt() is True
        assert PlayOutcome.STOLEN_BASE.is_interrupt() is True
        assert PlayOutcome.CAUGHT_STEALING.is_interrupt() is True
        assert PlayOutcome.BALK.is_interrupt() is True
        assert PlayOutcome.PICK_OFF.is_interrupt() is True

    def test_is_interrupt_false_for_normal_plays(self):
        """Normal plays are not interrupts."""
        assert PlayOutcome.SINGLE_1.is_interrupt() is False
        assert PlayOutcome.STRIKEOUT.is_interrupt() is False
        assert PlayOutcome.WALK.is_interrupt() is False

    def test_is_extra_base_hit(self):
        """Extra base hits are identified correctly."""
        assert PlayOutcome.DOUBLE_2.is_extra_base_hit() is True
        assert PlayOutcome.DOUBLE_3.is_extra_base_hit() is True
        assert PlayOutcome.DOUBLE_UNCAPPED.is_extra_base_hit() is True
        assert PlayOutcome.TRIPLE.is_extra_base_hit() is True
        assert PlayOutcome.HOMERUN.is_extra_base_hit() is True
        assert PlayOutcome.BP_HOMERUN.is_extra_base_hit() is True

    def test_is_extra_base_hit_false_for_singles(self):
        """Singles are not extra base hits."""
        assert PlayOutcome.SINGLE_1.is_extra_base_hit() is False
        assert PlayOutcome.SINGLE_UNCAPPED.is_extra_base_hit() is False
        assert PlayOutcome.BP_SINGLE.is_extra_base_hit() is False


class TestGetBasesAdvanced:
    """Tests for get_bases_advanced() method."""

    def test_singles_advance_one_base(self):
        """Singles advance one base."""
        assert PlayOutcome.SINGLE_1.get_bases_advanced() == 1
        assert PlayOutcome.SINGLE_2.get_bases_advanced() == 1
        assert PlayOutcome.SINGLE_UNCAPPED.get_bases_advanced() == 1
        assert PlayOutcome.BP_SINGLE.get_bases_advanced() == 1

    def test_doubles_advance_two_bases(self):
        """Doubles advance two bases."""
        assert PlayOutcome.DOUBLE_2.get_bases_advanced() == 2
        assert PlayOutcome.DOUBLE_3.get_bases_advanced() == 2
        assert PlayOutcome.DOUBLE_UNCAPPED.get_bases_advanced() == 2

    def test_triples_advance_three_bases(self):
        """Triples advance three bases."""
        assert PlayOutcome.TRIPLE.get_bases_advanced() == 3

    def test_homeruns_advance_four_bases(self):
        """Home runs advance four bases."""
        assert PlayOutcome.HOMERUN.get_bases_advanced() == 4
        assert PlayOutcome.BP_HOMERUN.get_bases_advanced() == 4

    def test_outs_advance_zero_bases(self):
        """Outs advance zero bases."""
        assert PlayOutcome.STRIKEOUT.get_bases_advanced() == 0
        assert PlayOutcome.GROUNDBALL_A.get_bases_advanced() == 0
        assert PlayOutcome.FLYOUT_A.get_bases_advanced() == 0

    def test_walks_advance_zero_bases(self):
        """Walks advance zero bases (forced advancement handled separately)."""
        assert PlayOutcome.WALK.get_bases_advanced() == 0
        assert PlayOutcome.INTENTIONAL_WALK.get_bases_advanced() == 0

    def test_interrupts_advance_zero_bases(self):
        """Interrupts advance zero bases (advancement handled by interrupt logic)."""
        assert PlayOutcome.WILD_PITCH.get_bases_advanced() == 0
        assert PlayOutcome.STOLEN_BASE.get_bases_advanced() == 0


class TestPlayOutcomeValues:
    """Tests for PlayOutcome string values."""

    def test_outcome_string_values(self):
        """Outcome values match expected strings."""
        assert PlayOutcome.STRIKEOUT.value == "strikeout"
        assert PlayOutcome.SINGLE_1.value == "single_1"
        assert PlayOutcome.SINGLE_UNCAPPED.value == "single_uncapped"
        assert PlayOutcome.WILD_PITCH.value == "wild_pitch"
        assert PlayOutcome.BP_HOMERUN.value == "bp_homerun"

    def test_can_create_from_string(self):
        """Can create PlayOutcome from string value."""
        outcome = PlayOutcome("strikeout")
        assert outcome == PlayOutcome.STRIKEOUT

    def test_uncapped_uses_code_friendly_names(self):
        """Uncapped outcomes use underscores not parentheses."""
        assert "(" not in PlayOutcome.SINGLE_UNCAPPED.value
        assert "(" not in PlayOutcome.DOUBLE_UNCAPPED.value
        assert "_" in PlayOutcome.SINGLE_UNCAPPED.value


class TestPlayOutcomeCompleteness:
    """Tests to ensure all outcome categories are covered."""

    def test_all_hits_categorized(self):
        """All hit outcomes are properly categorized."""
        hit_outcomes = {
            PlayOutcome.SINGLE_1, PlayOutcome.SINGLE_2, PlayOutcome.DOUBLE_2,
            PlayOutcome.DOUBLE_3, PlayOutcome.TRIPLE,
            PlayOutcome.HOMERUN, PlayOutcome.SINGLE_UNCAPPED,
            PlayOutcome.DOUBLE_UNCAPPED, PlayOutcome.BP_HOMERUN,
            PlayOutcome.BP_SINGLE
        }
        for outcome in hit_outcomes:
            assert outcome.is_hit() is True, f"{outcome} should be a hit"

    def test_all_outs_categorized(self):
        """All out outcomes are properly categorized."""
        out_outcomes = {
            PlayOutcome.STRIKEOUT,
            PlayOutcome.GROUNDBALL_A, PlayOutcome.GROUNDBALL_B, PlayOutcome.GROUNDBALL_C,
            PlayOutcome.FLYOUT_A, PlayOutcome.FLYOUT_B, PlayOutcome.FLYOUT_C,
            PlayOutcome.LINEOUT, PlayOutcome.POPOUT,
            PlayOutcome.CAUGHT_STEALING, PlayOutcome.PICK_OFF,
            PlayOutcome.BP_FLYOUT, PlayOutcome.BP_LINEOUT
        }
        for outcome in out_outcomes:
            assert outcome.is_out() is True, f"{outcome} should be an out"

    def test_all_interrupts_categorized(self):
        """All interrupt outcomes are properly categorized."""
        interrupt_outcomes = {
            PlayOutcome.WILD_PITCH, PlayOutcome.PASSED_BALL,
            PlayOutcome.STOLEN_BASE, PlayOutcome.CAUGHT_STEALING,
            PlayOutcome.BALK, PlayOutcome.PICK_OFF
        }
        for outcome in interrupt_outcomes:
            assert outcome.is_interrupt() is True, f"{outcome} should be an interrupt"
