"""
Unit Tests for Dice Roll Types

Tests all roll type dataclasses: AbRoll, JumpRoll, FieldingRoll, D20Roll.
"""
import pytest
from uuid import uuid4
import pendulum

from app.core.roll_types import (
    RollType, DiceRoll, AbRoll, JumpRoll, FieldingRoll, D20Roll
)


class TestBaseDiceRoll:
    """Test base DiceRoll class"""

    def test_dice_roll_creation(self):
        """Test creating a base DiceRoll"""
        roll_id = "test123"
        game_id = uuid4()
        timestamp = pendulum.now('UTC')

        roll = DiceRoll(
            roll_id=roll_id,
            roll_type=RollType.D20,
            league_id="sba",
            timestamp=timestamp,
            game_id=game_id,
            team_id=1,
            player_id=101
        )

        assert roll.roll_id == roll_id
        assert roll.roll_type == RollType.D20
        assert roll.league_id == "sba"
        assert roll.game_id == game_id
        assert roll.team_id == 1
        assert roll.player_id == 101

    def test_dice_roll_to_dict(self):
        """Test DiceRoll serialization"""
        game_id = uuid4()
        timestamp = pendulum.now('UTC')

        roll = DiceRoll(
            roll_id="test123",
            roll_type=RollType.AB,
            league_id="pd",
            timestamp=timestamp,
            game_id=game_id
        )

        data = roll.to_dict()

        assert data["roll_id"] == "test123"
        assert data["roll_type"] == "ab"
        assert data["league_id"] == "pd"
        assert data["game_id"] == str(game_id)
        assert "timestamp" in data


class TestAbRoll:
    """Test AtBat roll class"""

    def test_ab_roll_basic(self):
        """Test basic at-bat roll creation"""
        roll = AbRoll(
            roll_id="ab123",
            roll_type=RollType.AB,
            league_id="sba",
            timestamp=pendulum.now('UTC'),
            d6_one=3,
            d6_two_a=4,
            d6_two_b=2,
            chaos_d20=15,
            resolution_d20=8
        )

        assert roll.d6_one == 3
        assert roll.d6_two_a == 4
        assert roll.d6_two_b == 2
        assert roll.d6_two_total == 6  # Calculated in __post_init__
        assert roll.chaos_d20 == 15
        assert roll.resolution_d20 == 8
        assert not roll.check_wild_pitch
        assert not roll.check_passed_ball

    def test_ab_roll_wild_pitch_check(self):
        """Test wild pitch detection"""
        roll = AbRoll(
            roll_id="ab_wp",
            roll_type=RollType.AB,
            league_id="sba",
            timestamp=pendulum.now('UTC'),
            d6_one=3,
            d6_two_a=4,
            d6_two_b=2,
            chaos_d20=1,  # Wild pitch check
            resolution_d20=12
        )

        assert roll.check_wild_pitch
        assert not roll.check_passed_ball
        assert roll.resolution_d20 == 12  # Would be used to confirm WP

    def test_ab_roll_passed_ball_check(self):
        """Test passed ball detection"""
        roll = AbRoll(
            roll_id="ab_pb",
            roll_type=RollType.AB,
            league_id="pd",
            timestamp=pendulum.now('UTC'),
            d6_one=5,
            d6_two_a=1,
            d6_two_b=6,
            chaos_d20=2,  # Passed ball check
            resolution_d20=7
        )

        assert roll.check_passed_ball
        assert not roll.check_wild_pitch
        assert roll.resolution_d20 == 7  # Would be used to confirm PB

    def test_ab_roll_to_dict(self):
        """Test AbRoll serialization"""
        roll = AbRoll(
            roll_id="ab456",
            roll_type=RollType.AB,
            league_id="sba",
            timestamp=pendulum.now('UTC'),
            game_id=uuid4(),
            d6_one=2,
            d6_two_a=3,
            d6_two_b=5,
            chaos_d20=10,
            resolution_d20=14
        )

        data = roll.to_dict()

        assert data["roll_type"] == "ab"
        assert data["d6_one"] == 2
        assert data["d6_two_a"] == 3
        assert data["d6_two_b"] == 5
        assert data["d6_two_total"] == 8
        assert data["chaos_d20"] == 10
        assert data["resolution_d20"] == 14
        assert data["check_wild_pitch"] is False
        assert data["check_passed_ball"] is False

    def test_ab_roll_str_normal(self):
        """Test string representation for normal at-bat"""
        roll = AbRoll(
            roll_id="ab789",
            roll_type=RollType.AB,
            league_id="sba",
            timestamp=pendulum.now('UTC'),
            d6_one=4,
            d6_two_a=3,
            d6_two_b=2,
            chaos_d20=12,
            resolution_d20=18
        )

        result = str(roll)
        assert "4" in result
        assert "5" in result  # d6_two_total
        # chaos_d20 not in string, only resolution_d20
        assert "18" in result

    def test_ab_roll_str_wild_pitch(self):
        """Test string representation for wild pitch check"""
        roll = AbRoll(
            roll_id="ab_wp2",
            roll_type=RollType.AB,
            league_id="sba",
            timestamp=pendulum.now('UTC'),
            d6_one=3,
            d6_two_a=4,
            d6_two_b=1,
            chaos_d20=1,
            resolution_d20=9
        )

        result = str(roll)
        assert "WP" in result
        # New format: "WP check" - no dice values displayed


class TestJumpRoll:
    """Test Jump roll class"""

    def test_jump_roll_normal(self):
        """Test normal jump roll"""
        roll = JumpRoll(
            roll_id="jump1",
            roll_type=RollType.JUMP,
            league_id="sba",
            timestamp=pendulum.now('UTC'),
            check_roll=10,
            jump_dice_a=4,
            jump_dice_b=3
        )

        assert roll.check_roll == 10
        assert roll.jump_dice_a == 4
        assert roll.jump_dice_b == 3
        assert roll.jump_total == 7  # Calculated
        assert not roll.is_pickoff_check
        assert not roll.is_balk_check
        assert roll.resolution_roll is None

    def test_jump_roll_pickoff(self):
        """Test pickoff check"""
        roll = JumpRoll(
            roll_id="jump_po",
            roll_type=RollType.JUMP,
            league_id="sba",
            timestamp=pendulum.now('UTC'),
            check_roll=1,
            resolution_roll=15
        )

        assert roll.check_roll == 1
        assert roll.is_pickoff_check
        assert not roll.is_balk_check
        assert roll.resolution_roll == 15
        assert roll.jump_dice_a is None
        assert roll.jump_dice_b is None
        assert roll.jump_total is None

    def test_jump_roll_balk(self):
        """Test balk check"""
        roll = JumpRoll(
            roll_id="jump_balk",
            roll_type=RollType.JUMP,
            league_id="pd",
            timestamp=pendulum.now('UTC'),
            check_roll=2,
            resolution_roll=8
        )

        assert roll.check_roll == 2
        assert roll.is_balk_check
        assert not roll.is_pickoff_check
        assert roll.resolution_roll == 8
        assert roll.jump_total is None

    def test_jump_roll_to_dict(self):
        """Test JumpRoll serialization"""
        roll = JumpRoll(
            roll_id="jump2",
            roll_type=RollType.JUMP,
            league_id="sba",
            timestamp=pendulum.now('UTC'),
            check_roll=12,
            jump_dice_a=5,
            jump_dice_b=6
        )

        data = roll.to_dict()

        assert data["roll_type"] == "jump"
        assert data["check_roll"] == 12
        assert data["jump_dice_a"] == 5
        assert data["jump_dice_b"] == 6
        assert data["jump_total"] == 11
        assert data["is_pickoff_check"] is False
        assert data["is_balk_check"] is False

    def test_jump_roll_str_normal(self):
        """Test string representation for normal jump"""
        roll = JumpRoll(
            roll_id="jump3",
            roll_type=RollType.JUMP,
            league_id="sba",
            timestamp=pendulum.now('UTC'),
            check_roll=15,
            jump_dice_a=3,
            jump_dice_b=4
        )

        result = str(roll)
        assert "7" in result  # jump_total
        assert "3" in result
        assert "4" in result

    def test_jump_roll_str_pickoff(self):
        """Test string representation for pickoff"""
        roll = JumpRoll(
            roll_id="jump_po2",
            roll_type=RollType.JUMP,
            league_id="sba",
            timestamp=pendulum.now('UTC'),
            check_roll=1,
            resolution_roll=11
        )

        result = str(roll)
        assert "Pickoff Check" in result
        assert "11" in result


class TestFieldingRoll:
    """Test Fielding roll class"""

    def test_fielding_roll_sba_normal(self):
        """Test SBA fielding roll (no rare play)"""
        roll = FieldingRoll(
            roll_id="field1",
            roll_type=RollType.FIELDING,
            league_id="sba",
            timestamp=pendulum.now('UTC'),
            position="SS",
            d20=12,
            d6_one=3,
            d6_two=4,
            d6_three=2,
            d100=50
        )

        assert roll.position == "SS"
        assert roll.d20 == 12
        assert roll.error_total == 9  # 3+4+2
        assert roll.d100 == 50
        assert not roll.is_rare_play  # d100 != 1

    def test_fielding_roll_sba_rare_play(self):
        """Test SBA rare play (d100 == 1)"""
        roll = FieldingRoll(
            roll_id="field_rare_sba",
            roll_type=RollType.FIELDING,
            league_id="sba",
            timestamp=pendulum.now('UTC'),
            position="CF",
            d20=15,
            d6_one=2,
            d6_two=3,
            d6_three=4,
            d100=1  # Rare play!
        )

        assert roll.is_rare_play
        assert roll.d100 == 1

    def test_fielding_roll_pd_normal(self):
        """Test PD fielding roll (no rare play)"""
        roll = FieldingRoll(
            roll_id="field_pd1",
            roll_type=RollType.FIELDING,
            league_id="pd",
            timestamp=pendulum.now('UTC'),
            position="1B",
            d20=8,
            d6_one=2,
            d6_two=3,
            d6_three=1,
            d100=75  # Doesn't matter for PD
        )

        assert roll.error_total == 6
        assert not roll.is_rare_play  # error_total != 5

    def test_fielding_roll_pd_rare_play(self):
        """Test PD rare play (error_total == 5)"""
        roll = FieldingRoll(
            roll_id="field_rare_pd",
            roll_type=RollType.FIELDING,
            league_id="pd",
            timestamp=pendulum.now('UTC'),
            position="3B",
            d20=14,
            d6_one=1,
            d6_two=2,
            d6_three=2,
            d100=99  # Doesn't matter for PD
        )

        assert roll.error_total == 5
        assert roll.is_rare_play  # error_total == 5 for PD

    def test_fielding_roll_invalid_league(self):
        """Test that invalid league raises error"""
        with pytest.raises(ValueError, match="Unknown league_id"):
            FieldingRoll(
                roll_id="field_bad",
                roll_type=RollType.FIELDING,
                league_id="invalid",
                timestamp=pendulum.now('UTC'),
                position="SS",
                d20=10,
                d6_one=3,
                d6_two=2,
                d6_three=4,
                d100=50
            )

    def test_fielding_roll_to_dict(self):
        """Test FieldingRoll serialization"""
        roll = FieldingRoll(
            roll_id="field2",
            roll_type=RollType.FIELDING,
            league_id="sba",
            timestamp=pendulum.now('UTC'),
            position="RF",
            d20=18,
            d6_one=5,
            d6_two=1,
            d6_three=3,
            d100=42
        )

        data = roll.to_dict()

        assert data["roll_type"] == "fielding"
        assert data["position"] == "RF"
        assert data["d20"] == 18
        assert data["d6_one"] == 5
        assert data["d6_two"] == 1
        assert data["d6_three"] == 3
        assert data["d100"] == 42
        assert data["error_total"] == 9
        assert data["is_rare_play"] is False

    def test_fielding_roll_str_normal(self):
        """Test string representation for normal fielding"""
        roll = FieldingRoll(
            roll_id="field3",
            roll_type=RollType.FIELDING,
            league_id="sba",
            timestamp=pendulum.now('UTC'),
            position="LF",
            d20=9,
            d6_one=2,
            d6_two=4,
            d6_three=3,
            d100=23
        )

        result = str(roll)
        assert "LF" in result
        assert "d20=9" in result
        assert "error=9" in result
        assert "RARE PLAY" not in result

    def test_fielding_roll_str_rare(self):
        """Test string representation for rare play"""
        roll = FieldingRoll(
            roll_id="field_rare2",
            roll_type=RollType.FIELDING,
            league_id="sba",
            timestamp=pendulum.now('UTC'),
            position="P",
            d20=11,
            d6_one=1,
            d6_two=1,
            d6_three=1,
            d100=1
        )

        result = str(roll)
        assert "RARE PLAY" in result


class TestD20Roll:
    """Test D20 roll class"""

    def test_d20_roll_basic(self):
        """Test basic d20 roll"""
        roll = D20Roll(
            roll_id="d20_1",
            roll_type=RollType.D20,
            league_id="sba",
            timestamp=pendulum.now('UTC'),
            roll=15
        )

        assert roll.roll == 15
        assert roll.roll_type == RollType.D20

    def test_d20_roll_to_dict(self):
        """Test D20Roll serialization"""
        game_id = uuid4()
        roll = D20Roll(
            roll_id="d20_2",
            roll_type=RollType.D20,
            league_id="pd",
            timestamp=pendulum.now('UTC'),
            game_id=game_id,
            roll=8
        )

        data = roll.to_dict()

        assert data["roll_type"] == "d20"
        assert data["roll"] == 8
        assert data["game_id"] == str(game_id)

    def test_d20_roll_str(self):
        """Test string representation"""
        roll = D20Roll(
            roll_id="d20_3",
            roll_type=RollType.D20,
            league_id="sba",
            timestamp=pendulum.now('UTC'),
            roll=20
        )

        result = str(roll)
        assert result == "20"


class TestRollTypeEnum:
    """Test RollType enum"""

    def test_roll_type_values(self):
        """Test enum values"""
        assert RollType.AB == "ab"
        assert RollType.JUMP == "jump"
        assert RollType.FIELDING == "fielding"
        assert RollType.D20 == "d20"

    def test_roll_type_usage(self):
        """Test using enum in roll creation"""
        roll = D20Roll(
            roll_id="test",
            roll_type=RollType.D20,
            league_id="sba",
            timestamp=pendulum.now('UTC'),
            roll=10
        )

        assert roll.roll_type == RollType.D20
        assert roll.roll_type.value == "d20"
