"""
Tests for Tarot deck and card classes.
"""

import pytest

from src.tarot.attributes import CardImage, Meaning, Suit
from src.tarot.deck import AceCard, Card, CourtCard, Deck, MajorCard, MinorCard, PipCard


class TestCard:
    def test_card_creation(self):
        card = Card(1, "Test Card", Meaning("Upright", "Reversed"), "Minor")
        assert card.number == 1
        assert card.name == "Test Card"
        assert card.meaning.upright == "Upright"

    def test_card_str(self):
        card = Card(1, "Test Card", Meaning("Upright", "Reversed"), "Minor")
        assert str(card) == "1. Test Card"

    def test_card_repr(self):
        card = Card(1, "Test Card", Meaning("Upright", "Reversed"), "Minor")
        assert repr(card) == "Card(1, 'Test Card')"


class TestMajorCard:
    def test_major_card_creation(self):
        card = MajorCard(
            number=1,
            name="The Magician",
            meaning=Meaning("Upright", "Reversed"),
            arcana="Major",
            kabbalistic_number=1,
        )
        assert card.number == 1
        assert card.arcana == "Major"

    def test_major_card_invalid_low(self):
        with pytest.raises(ValueError):
            MajorCard(
                number=1,
                name="Test",
                meaning=Meaning("Up", "Rev"),
                arcana="Major",
                kabbalistic_number=-1,
            )

    def test_major_card_invalid_high(self):
        with pytest.raises(ValueError):
            MajorCard(
                number=1,
                name="Test",
                meaning=Meaning("Up", "Rev"),
                arcana="Major",
                kabbalistic_number=22,
            )

    def test_major_card_valid_range(self):
        for i in range(22):
            card = MajorCard(
                number=i, name=f"Card {i}", meaning=Meaning("Up", "Rev"), arcana="Major"
            )
            assert card.number == i


class TestMinorCard:
    def test_minor_card_creation(self):
        suit = Suit("Cups", "Water", "Chalice", 1)
        card = MinorCard(
            number=1,
            name="Ace of Cups",
            meaning=Meaning("Upright", "Reversed"),
            arcana="Minor",
            suit=suit,
            pip=1,
        )
        assert card.number == 1
        assert card.suit.name == "Cups"
        assert card.pip == 1

    def test_minor_card_invalid_pip_low(self):
        suit = Suit("Cups", "Water", "Chalice", 1)
        with pytest.raises(ValueError):
            PipCard(
                number=1,
                name="Test",
                meaning=Meaning("Up", "Rev"),
                arcana="Minor",
                suit=suit,
                pip=0,
            )

    def test_minor_card_invalid_pip_high(self):
        suit = Suit("Cups", "Water", "Chalice", 1)
        with pytest.raises(ValueError):
            PipCard(
                number=1,
                name="Test",
                meaning=Meaning("Up", "Rev"),
                arcana="Minor",
                suit=suit,
                pip=15,
            )

    def test_minor_card_valid_pips(self):
        suit = Suit("Cups", "Water", "Chalice", 1)
        for i in range(1, 15):
            card = MinorCard(
                number=i,
                name=f"Card {i}",
                meaning=Meaning("Up", "Rev"),
                arcana="Minor",
                suit=suit,
                pip=i,
            )
            assert card.pip == i


class TestDeck:
    def test_deck_creation(self):
        deck = Deck()
        assert len(deck.cards) == 78

    def test_deck_major_arcana_count(self):
        deck = Deck()
        major_cards = [c for c in deck.cards if c.arcana == "Major"]
        assert len(major_cards) == 22

    def test_deck_minor_arcana_count(self):
        deck = Deck()
        minor_cards = [c for c in deck.cards if c.arcana == "Minor"]
        assert len(minor_cards) == 56

    def test_deck_shuffle(self):
        deck1 = Deck()
        cards_before = [c.name for c in deck1.cards]

        deck1.shuffle()
        cards_after = [c.name for c in deck1.cards]

        # After shuffle, order should change (with high probability)
        # We don't assert they're different since shuffle could randomly give same order
        assert len(cards_after) == 78

    def test_deck_draw_single(self):
        deck = Deck()
        initial_count = len(deck.cards)

        drawn = deck.draw(1)

        assert len(drawn) == 1
        assert len(deck.cards) == initial_count - 1

    def test_deck_draw_multiple(self):
        deck = Deck()
        initial_count = len(deck.cards)

        drawn = deck.draw(5)

        assert len(drawn) == 5
        assert len(deck.cards) == initial_count - 5

    def test_deck_draw_too_many(self):
        deck = Deck()
        with pytest.raises(ValueError):
            deck.draw(100)

    def test_deck_draw_invalid_amount(self):
        deck = Deck()
        with pytest.raises(ValueError):
            deck.draw(0)

    def test_deck_reset(self):
        deck = Deck()
        deck.draw(5)
        assert len(deck.cards) < 78

        deck.reset()
        assert len(deck.cards) == 78

    def test_deck_remaining(self):
        deck = Deck()
        assert deck.remaining() == 78

        deck.draw(1)
        assert deck.remaining() == 77

    def test_deck_len(self):
        deck = Deck()
        assert len(deck) == 78

        deck.draw(1)
        assert len(deck) == 77

    def test_deck_repr(self):
        deck = Deck()
        assert "78 cards" in repr(deck)

        deck.draw(1)
        assert "77 cards" in repr(deck)

    def test_deck_all_suits(self):
        deck = Deck()
        suits = {c.suit.name for c in deck.cards if c.arcana == "Minor"}
        assert suits == {"Cups", "Pentacles", "Swords", "Wands"}

    def test_deck_unique_cards(self):
        deck = Deck()
        card_names = [c.name for c in deck.cards]
        assert len(card_names) == len(set(card_names))  # All unique
