"""
Unit tests for roster models (Pydantic).

Tests polymorphic roster link models for type safety across leagues.

Author: Claude
Date: 2025-10-22
"""

import pytest
from uuid import uuid4
from pydantic import ValidationError

from app.models.roster_models import (
    PdRosterLinkData,
    SbaRosterLinkData,
    RosterLinkCreate,
)


class TestPdRosterLinkData:
    """Test PD league roster link data model"""

    def test_create_valid_pd_roster_link(self):
        """Test creating valid PD roster link"""
        game_id = uuid4()
        data = PdRosterLinkData(
            game_id=game_id,
            card_id=123,
            team_id=1
        )

        assert data.game_id == game_id
        assert data.card_id == 123
        assert data.team_id == 1
        assert data.id is None  # Not yet saved to DB

    def test_pd_roster_link_with_id(self):
        """Test PD roster link with database ID"""
        game_id = uuid4()
        data = PdRosterLinkData(
            id=42,
            game_id=game_id,
            card_id=123,
            team_id=1
        )

        assert data.id == 42

    def test_pd_roster_link_invalid_card_id(self):
        """Test PD roster link rejects negative card_id"""
        game_id = uuid4()

        with pytest.raises(ValidationError) as exc_info:
            PdRosterLinkData(
                game_id=game_id,
                card_id=-1,
                team_id=1
            )

        assert "card_id must be positive" in str(exc_info.value)

    def test_pd_roster_link_zero_card_id(self):
        """Test PD roster link rejects zero card_id"""
        game_id = uuid4()

        with pytest.raises(ValidationError) as exc_info:
            PdRosterLinkData(
                game_id=game_id,
                card_id=0,
                team_id=1
            )

        assert "card_id must be positive" in str(exc_info.value)

    def test_pd_get_entity_id(self):
        """Test get_entity_id returns card_id"""
        data = PdRosterLinkData(
            game_id=uuid4(),
            card_id=123,
            team_id=1
        )

        assert data.get_entity_id() == 123

    def test_pd_get_entity_type(self):
        """Test get_entity_type returns 'card'"""
        data = PdRosterLinkData(
            game_id=uuid4(),
            card_id=123,
            team_id=1
        )

        assert data.get_entity_type() == "card"


class TestSbaRosterLinkData:
    """Test SBA league roster link data model"""

    def test_create_valid_sba_roster_link(self):
        """Test creating valid SBA roster link"""
        game_id = uuid4()
        data = SbaRosterLinkData(
            game_id=game_id,
            player_id=456,
            team_id=2
        )

        assert data.game_id == game_id
        assert data.player_id == 456
        assert data.team_id == 2
        assert data.id is None

    def test_sba_roster_link_with_id(self):
        """Test SBA roster link with database ID"""
        game_id = uuid4()
        data = SbaRosterLinkData(
            id=99,
            game_id=game_id,
            player_id=456,
            team_id=2
        )

        assert data.id == 99

    def test_sba_roster_link_invalid_player_id(self):
        """Test SBA roster link rejects negative player_id"""
        game_id = uuid4()

        with pytest.raises(ValidationError) as exc_info:
            SbaRosterLinkData(
                game_id=game_id,
                player_id=-5,
                team_id=2
            )

        assert "player_id must be positive" in str(exc_info.value)

    def test_sba_roster_link_zero_player_id(self):
        """Test SBA roster link rejects zero player_id"""
        game_id = uuid4()

        with pytest.raises(ValidationError) as exc_info:
            SbaRosterLinkData(
                game_id=game_id,
                player_id=0,
                team_id=2
            )

        assert "player_id must be positive" in str(exc_info.value)

    def test_sba_get_entity_id(self):
        """Test get_entity_id returns player_id"""
        data = SbaRosterLinkData(
            game_id=uuid4(),
            player_id=456,
            team_id=2
        )

        assert data.get_entity_id() == 456

    def test_sba_get_entity_type(self):
        """Test get_entity_type returns 'player'"""
        data = SbaRosterLinkData(
            game_id=uuid4(),
            player_id=456,
            team_id=2
        )

        assert data.get_entity_type() == "player"


class TestRosterLinkCreate:
    """Test RosterLinkCreate request model"""

    def test_create_with_card_id(self):
        """Test creating request with card_id only"""
        game_id = uuid4()
        data = RosterLinkCreate(
            game_id=game_id,
            team_id=1,
            card_id=123
        )

        assert data.game_id == game_id
        assert data.team_id == 1
        assert data.card_id == 123
        assert data.player_id is None

    def test_create_with_player_id(self):
        """Test creating request with player_id only"""
        game_id = uuid4()
        data = RosterLinkCreate(
            game_id=game_id,
            team_id=2,
            player_id=456
        )

        assert data.game_id == game_id
        assert data.team_id == 2
        assert data.player_id == 456
        assert data.card_id is None

    def test_create_with_both_ids_fails(self):
        """Test that providing both IDs raises error"""
        game_id = uuid4()

        with pytest.raises(ValidationError) as exc_info:
            RosterLinkCreate(
                game_id=game_id,
                team_id=1,
                card_id=123,
                player_id=456
            )

        assert "Exactly one of card_id or player_id must be provided" in str(exc_info.value)

    def test_create_with_neither_id_fails(self):
        """Test that providing neither ID raises error"""
        game_id = uuid4()

        with pytest.raises(ValidationError) as exc_info:
            RosterLinkCreate(
                game_id=game_id,
                team_id=1
            )

        assert "Exactly one of card_id or player_id must be provided" in str(exc_info.value)

    def test_invalid_team_id(self):
        """Test that negative team_id raises error"""
        game_id = uuid4()

        with pytest.raises(ValidationError) as exc_info:
            RosterLinkCreate(
                game_id=game_id,
                team_id=-1,
                card_id=123
            )

        assert "team_id must be positive" in str(exc_info.value)

    def test_zero_team_id(self):
        """Test that zero team_id raises error"""
        game_id = uuid4()

        with pytest.raises(ValidationError) as exc_info:
            RosterLinkCreate(
                game_id=game_id,
                team_id=0,
                player_id=456
            )

        assert "team_id must be positive" in str(exc_info.value)

    def test_to_pd_data_success(self):
        """Test converting to PdRosterLinkData"""
        game_id = uuid4()
        create_data = RosterLinkCreate(
            game_id=game_id,
            team_id=1,
            card_id=123
        )

        pd_data = create_data.to_pd_data()

        assert isinstance(pd_data, PdRosterLinkData)
        assert pd_data.game_id == game_id
        assert pd_data.team_id == 1
        assert pd_data.card_id == 123

    def test_to_pd_data_fails_without_card_id(self):
        """Test converting to PdRosterLinkData fails without card_id"""
        game_id = uuid4()
        create_data = RosterLinkCreate(
            game_id=game_id,
            team_id=1,
            player_id=456
        )

        with pytest.raises(ValueError) as exc_info:
            create_data.to_pd_data()

        assert "card_id required for PD roster" in str(exc_info.value)

    def test_to_sba_data_success(self):
        """Test converting to SbaRosterLinkData"""
        game_id = uuid4()
        create_data = RosterLinkCreate(
            game_id=game_id,
            team_id=2,
            player_id=456
        )

        sba_data = create_data.to_sba_data()

        assert isinstance(sba_data, SbaRosterLinkData)
        assert sba_data.game_id == game_id
        assert sba_data.team_id == 2
        assert sba_data.player_id == 456

    def test_to_sba_data_fails_without_player_id(self):
        """Test converting to SbaRosterLinkData fails without player_id"""
        game_id = uuid4()
        create_data = RosterLinkCreate(
            game_id=game_id,
            team_id=1,
            card_id=123
        )

        with pytest.raises(ValueError) as exc_info:
            create_data.to_sba_data()

        assert "player_id required for SBA roster" in str(exc_info.value)


class TestPolymorphicBehavior:
    """Test polymorphic patterns across roster types"""

    def test_both_types_have_get_entity_id(self):
        """Test both types implement get_entity_id"""
        pd_data = PdRosterLinkData(
            game_id=uuid4(),
            card_id=123,
            team_id=1
        )
        sba_data = SbaRosterLinkData(
            game_id=uuid4(),
            player_id=456,
            team_id=2
        )

        # Both should have the method
        assert callable(pd_data.get_entity_id)
        assert callable(sba_data.get_entity_id)

        # Different return values
        assert pd_data.get_entity_id() == 123
        assert sba_data.get_entity_id() == 456

    def test_both_types_have_get_entity_type(self):
        """Test both types implement get_entity_type"""
        pd_data = PdRosterLinkData(
            game_id=uuid4(),
            card_id=123,
            team_id=1
        )
        sba_data = SbaRosterLinkData(
            game_id=uuid4(),
            player_id=456,
            team_id=2
        )

        # Different return values
        assert pd_data.get_entity_type() == "card"
        assert sba_data.get_entity_type() == "player"
