"""
Unit Tests for Play Resolver

Tests play outcome resolution with new outcome-first architecture.
"""
import pytest
from uuid import uuid4
import pendulum

from app.core.play_resolver import PlayResolver, PlayResult
from app.config import PlayOutcome
from app.core.roll_types import AbRoll, RollType
from app.models.game_models import GameState, LineupPlayerState, DefensiveDecision, OffensiveDecision, ManualOutcomeSubmission


# Helper to create mock AbRoll
def create_mock_ab_roll(game_id=None) -> AbRoll:
    """Create a mock AbRoll for testing"""
    return AbRoll(
        roll_type=RollType.AB,
        roll_id="test_roll_id",
        timestamp=pendulum.now('UTC'),
        league_id="sba",
        game_id=game_id,
        d6_one=3,
        d6_two_a=2,
        d6_two_b=4,
        chaos_d20=10,
        resolution_d20=10
    )


class TestPlayResolverInit:
    """Test PlayResolver initialization and configuration"""

    def test_init_sba_manual(self):
        """Test creating SBA resolver in manual mode"""
        resolver = PlayResolver(league_id="sba", auto_mode=False)
        assert resolver.league_id == "sba"
        assert resolver.auto_mode is False
        assert resolver.result_chart is None  # Manual mode doesn't use chart

    def test_init_pd_manual(self):
        """Test creating PD resolver in manual mode"""
        resolver = PlayResolver(league_id="pd", auto_mode=False)
        assert resolver.league_id == "pd"
        assert resolver.auto_mode is False
        assert resolver.result_chart is None

    def test_init_pd_auto(self):
        """Test creating PD resolver in auto mode"""
        resolver = PlayResolver(league_id="pd", auto_mode=True)
        assert resolver.league_id == "pd"
        assert resolver.auto_mode is True
        assert resolver.result_chart is not None  # Auto mode uses chart

    def test_init_sba_auto_raises(self):
        """Test that SBA with auto mode raises error"""
        with pytest.raises(ValueError, match="Auto mode not supported for sba"):
            PlayResolver(league_id="sba", auto_mode=True)


class TestResolveOutcome:
    """Test core resolve_outcome method"""

    def test_strikeout(self):
        """Test strikeout resolution"""
        resolver = PlayResolver(league_id="sba", auto_mode=False)
        state = GameState(
            game_id=uuid4(),
            league_id="sba",
            home_team_id=1,
            away_team_id=2,
            current_batter=LineupPlayerState(lineup_id=1, card_id=1*100, position="CF", batting_order=1)
        )
        ab_roll = create_mock_ab_roll(state.game_id)

        result = resolver.resolve_outcome(
            outcome=PlayOutcome.STRIKEOUT,
            hit_location=None,
            state=state,
            defensive_decision=DefensiveDecision(),
            offensive_decision=OffensiveDecision(),
            ab_roll=ab_roll
        )

        assert result.outcome == PlayOutcome.STRIKEOUT
        assert result.outs_recorded == 1
        assert result.runs_scored == 0
        assert result.batter_result is None
        assert result.runners_advanced == []
        assert result.is_out is True
        assert result.is_hit is False
        assert result.hit_location is None

    def test_walk(self):
        """Test walk resolution"""
        resolver = PlayResolver(league_id="sba", auto_mode=False)
        state = GameState(
            game_id=uuid4(),
            league_id="sba",
            home_team_id=1,
            away_team_id=2,
            current_batter=LineupPlayerState(lineup_id=1, card_id=1*100, position="CF", batting_order=1)
        )
        ab_roll = create_mock_ab_roll(state.game_id)

        result = resolver.resolve_outcome(
            outcome=PlayOutcome.WALK,
            hit_location=None,
            state=state,
            defensive_decision=DefensiveDecision(),
            offensive_decision=OffensiveDecision(),
            ab_roll=ab_roll
        )

        assert result.outcome == PlayOutcome.WALK
        assert result.outs_recorded == 0
        assert result.runs_scored == 0
        assert result.batter_result == 1
        assert result.is_walk is True
        assert result.hit_location is None

    def test_walk_bases_loaded(self):
        """Test walk with bases loaded forces run home"""
        resolver = PlayResolver(league_id="sba", auto_mode=False)
        state = GameState(
            game_id=uuid4(),
            league_id="sba",
            home_team_id=1,
            away_team_id=2,
            current_batter=LineupPlayerState(lineup_id=1, card_id=1*100, position="CF", batting_order=1),
            on_first=LineupPlayerState(lineup_id=1, card_id=101, position="CF", batting_order=1),
            on_second=LineupPlayerState(lineup_id=2, card_id=102, position="RF", batting_order=2),
            on_third=LineupPlayerState(lineup_id=3, card_id=103, position="SS", batting_order=3)
        )
        ab_roll = create_mock_ab_roll(state.game_id)

        result = resolver.resolve_outcome(
            outcome=PlayOutcome.WALK,
            hit_location=None,
            state=state,
            defensive_decision=DefensiveDecision(),
            offensive_decision=OffensiveDecision(),
            ab_roll=ab_roll
        )

        assert result.runs_scored == 1  # Runner on 3rd forced home
        # Check runner advancement using RunnerAdvancementData attributes
        assert any(adv.from_base == 3 and adv.to_base == 4 for adv in result.runners_advanced)

    def test_hit_by_pitch(self):
        """
        Test HBP (Hit By Pitch) resolution.

        HBP works identically to walk for runner advancement, but is NOT
        classified as a walk for baseball statistics purposes.
        """
        resolver = PlayResolver(league_id="sba", auto_mode=False)
        state = GameState(
            game_id=uuid4(),
            league_id="sba",
            home_team_id=1,
            away_team_id=2,
            current_batter=LineupPlayerState(lineup_id=1, card_id=100, position="CF", batting_order=1)
        )
        ab_roll = create_mock_ab_roll(state.game_id)

        result = resolver.resolve_outcome(
            outcome=PlayOutcome.HIT_BY_PITCH,
            hit_location=None,
            state=state,
            defensive_decision=DefensiveDecision(),
            offensive_decision=OffensiveDecision(),
            ab_roll=ab_roll
        )

        assert result.outcome == PlayOutcome.HIT_BY_PITCH
        assert result.outs_recorded == 0
        assert result.runs_scored == 0
        assert result.batter_result == 1  # Batter to first
        assert result.is_walk is False  # HBP is NOT classified as walk
        assert result.is_hit is False
        assert result.description == "Hit by pitch"

    def test_hit_by_pitch_bases_loaded(self):
        """
        Test HBP with bases loaded forces run home.

        Same forced runner advancement logic as walk.
        """
        resolver = PlayResolver(league_id="sba", auto_mode=False)
        state = GameState(
            game_id=uuid4(),
            league_id="sba",
            home_team_id=1,
            away_team_id=2,
            current_batter=LineupPlayerState(lineup_id=1, card_id=100, position="CF", batting_order=1),
            on_first=LineupPlayerState(lineup_id=2, card_id=101, position="CF", batting_order=2),
            on_second=LineupPlayerState(lineup_id=3, card_id=102, position="RF", batting_order=3),
            on_third=LineupPlayerState(lineup_id=4, card_id=103, position="SS", batting_order=4)
        )
        ab_roll = create_mock_ab_roll(state.game_id)

        result = resolver.resolve_outcome(
            outcome=PlayOutcome.HIT_BY_PITCH,
            hit_location=None,
            state=state,
            defensive_decision=DefensiveDecision(),
            offensive_decision=OffensiveDecision(),
            ab_roll=ab_roll
        )

        assert result.runs_scored == 1  # Runner on 3rd forced home
        # Check runner advancement using RunnerAdvancementData attributes
        assert any(adv.from_base == 3 and adv.to_base == 4 for adv in result.runners_advanced)

    def test_groundball_uses_runner_advancement(self):
        """Test that groundballs delegate to RunnerAdvancement"""
        resolver = PlayResolver(league_id="sba", auto_mode=False)
        state = GameState(
            game_id=uuid4(),
            league_id="sba",
            home_team_id=1,
            away_team_id=2,
            current_batter=LineupPlayerState(lineup_id=1, card_id=1*100, position="CF", batting_order=1)
        )
        ab_roll = create_mock_ab_roll(state.game_id)

        result = resolver.resolve_outcome(
            outcome=PlayOutcome.GROUNDBALL_C,
            hit_location="SS",
            state=state,
            defensive_decision=DefensiveDecision(),
            offensive_decision=OffensiveDecision(),
            ab_roll=ab_roll
        )

        # RunnerAdvancement should have been called
        assert result.outcome == PlayOutcome.GROUNDBALL_C
        assert result.hit_location == "SS"
        # Result should have outs/runs from RunnerAdvancement
        assert isinstance(result.outs_recorded, int)
        assert isinstance(result.runs_scored, int)

    def test_homerun_grand_slam(self):
        """Test grand slam homerun"""
        resolver = PlayResolver(league_id="sba", auto_mode=False)
        state = GameState(
            game_id=uuid4(),
            league_id="sba",
            home_team_id=1,
            away_team_id=2,
            current_batter=LineupPlayerState(lineup_id=1, card_id=1*100, position="CF", batting_order=1),
            on_first=LineupPlayerState(lineup_id=1, card_id=101, position="CF", batting_order=1),
            on_second=LineupPlayerState(lineup_id=2, card_id=102, position="RF", batting_order=2),
            on_third=LineupPlayerState(lineup_id=3, card_id=103, position="SS", batting_order=3)
        )
        ab_roll = create_mock_ab_roll(state.game_id)

        result = resolver.resolve_outcome(
            outcome=PlayOutcome.HOMERUN,
            hit_location=None,
            state=state,
            defensive_decision=DefensiveDecision(),
            offensive_decision=OffensiveDecision(),
            ab_roll=ab_roll
        )

        assert result.runs_scored == 4  # 3 runners + batter
        assert result.batter_result == 4
        assert result.is_hit is True
