from unittest import TestCase

from server.model.card import Card, lowest_value_and_rest
from server.model.color import Color
from server.model.value import Value
from server.model.deck import Deck
from server.model.hand import Hand
from server.model.hands import full, brelan, pair, single, double_pair, carre
from server.model.known import ACE_OF_HEARTS, PAIR_ACE, SINGLE_ACE, DOUBLE_PAIR_ACE, BRELAN_ACE, FULL_ACE
from server.model.players import RandomPlayer


class TestDeck(TestCase):

    def setUp(self) -> None:
        super().setUp()
        self.deck = Deck()

    def testInitDeck(self):
        self.assertEqual(52, len(self.deck), "52 cards")
        self.assertEqual(52, len(set(self.deck.cards)), "unique cards")

    def testRandomCard(self):
        cards = []
        for i in range(len(self.deck)):
            cards.append(self.deck.random_card())

        self.assertEqual(52, len(set(cards)), "draw each card once")


class TestPlayer(TestCase):

    def setUp(self) -> None:
        super().setUp()
        self.player = RandomPlayer()

    def testHand(self):
        self.assertEqual(0, len(self.player.hand), "Begin no cards")
        self.assertTrue(len(self.player.name), "Has a name")

        self.player.give(ACE_OF_HEARTS)

        self.assertEqual(1, len(self.player.hand), "Gave one card")
        self.assertEqual(Value.Ace, self.player.hand[0].value, "Is Ace")
        self.assertEqual(Color.Hearts, self.player.hand[0].color, "of Hearts")

    def testTricheur(self):
        with self.assertRaises(ValueError, msg="Giving someone a card they already have is an error"):
            self.player.give(ACE_OF_HEARTS)
            self.player.give(ACE_OF_HEARTS)

    def testDefeats(self):
        pass


class TestHand(TestCase):
    def setUp(self) -> None:
        self.hand = Hand()

    def testSimple(self):
        low_value, other_values = lowest_value_and_rest()

        for value in other_values:
            high_hand = single(value)
            low_hand = single(low_value)

            self.assertGreater(high_hand.value(), low_hand.value())

    def testPair(self):
        low_value, other_values = lowest_value_and_rest()

        for value in other_values:
            high_hand: Hand = pair(value)
            low_hand: Hand = pair(low_value)
            single_hand: Hand = single(Value.Ace)

            high_score = high_hand.value()
            low_score = low_hand.value()
            single_score = single_hand.value()

            self.assertGreater(high_score, low_score, f"Pair[{high_hand}] > Pair[{low_hand}]]")
            self.assertGreater(high_score, single_score, f"Pair[{high_hand}] > Ace")

    def testDoublePair(self):
        low_value, other_values = lowest_value_and_rest()

        for value in other_values:
            high_hand: Hand = double_pair(value)
            low_hand: Hand = double_pair(low_value)
            pair_hand: Hand = PAIR_ACE
            single_hand: Hand = SINGLE_ACE

            high_score = high_hand.value()
            low_score = low_hand.value()
            pair_score = pair_hand.value()
            single_score = single_hand.value()

            # Consider case when we compare equal double pairs
            low_cards = [c.value for c in low_hand.cards]
            if all([card.value in low_cards for card in high_hand.cards]):
                self.assertEqual(high_score, low_score, f"DoublePair[{high_hand}] == DoublePair[{low_hand}]")
            else:
                self.assertGreater(high_score, low_score, f"DoublePair[{high_hand}] > DoublePair[{low_hand}]")
            self.assertGreater(high_score, pair_score, f"DoublePair[{high_hand}] > Pair[Ace]")
            self.assertGreater(high_score, single_score, f"DoublePair[{high_hand}] > Ace")

    def testBrelan(self):
        low_value, other_values = lowest_value_and_rest()

        for value in other_values:
            high_hand: Hand = brelan(value)
            low_hand: Hand = brelan(low_value)
            double_pair_hand: Hand = DOUBLE_PAIR_ACE
            pair_hand: Hand = PAIR_ACE
            single_hand: Hand = SINGLE_ACE

            high_score = high_hand.value()
            low_score = low_hand.value()
            double_pair_score = double_pair_hand.value()
            pair_score = pair_hand.value()
            single_score = single_hand.value()

            self.assertGreater(high_score, low_score, f"Brelan[{high_hand}] > Brelan[{low_hand}]]")
            self.assertGreater(high_score, double_pair_score, f"Brelan[{high_hand}] > Pair[Ace]")
            self.assertGreater(high_score, pair_score, f"Brelan[{high_hand}] > Pair[Ace]")
            self.assertGreater(high_score, single_score, f"Brelan[{high_hand}] > Ace")

    def testFull(self):
        low_value, other_values = lowest_value_and_rest()

        for full_aux in other_values:  # Full_aux: brelan
            full_options = other_values.copy()
            full_options.remove(full_aux)
            full_options.append(low_value)

            for full_par in full_options:  # Full_des: paire
                high_hand: Hand = full(full_aux, full_par)
                low_hand: Hand = full(low_value, full_par)
                brelan_hand: Hand = BRELAN_ACE
                double_pair_hand: Hand = DOUBLE_PAIR_ACE
                pair_hand: Hand = PAIR_ACE
                single_hand: Hand = SINGLE_ACE

                high_score = high_hand.value()
                low_score = low_hand.value()
                brelan_score = brelan_hand.value()
                double_pair_score = double_pair_hand.value()
                pair_score = pair_hand.value()
                single_score = single_hand.value()

                self.assertGreater(high_score, low_score, f"Full[{high_hand}] > Full[{low_hand}]]")
                self.assertGreater(high_score, brelan_score, f"Full[{high_hand}] > Brelan[Ace]")
                self.assertGreater(high_score, double_pair_score, f"Full[{high_hand}] > Pair[Ace]")
                self.assertGreater(high_score, pair_score, f"Full[{high_hand}] > Pair[Ace]")
                self.assertGreater(high_score, single_score, f"Full[{high_hand}] > Ace")

    def testCarre(self):
        low_value, other_values = lowest_value_and_rest()

        for value in other_values:
            high_hand: Hand = carre(value)
            low_hand: Hand = carre(low_value)
            full_hand: Hand = FULL_ACE
            brelan_hand: Hand = BRELAN_ACE
            double_pair_hand: Hand = DOUBLE_PAIR_ACE
            pair_hand: Hand = PAIR_ACE
            single_hand: Hand = SINGLE_ACE

            high_score = high_hand.value()
            low_score = low_hand.value()
            full_score = full_hand.value()
            brelan_score = brelan_hand.value()
            double_pair_score = double_pair_hand.value()
            pair_score = pair_hand.value()
            single_score = single_hand.value()

            self.assertGreater(high_score, low_score, f"Carre[{high_hand}] > Carre[{low_hand}]]")
            self.assertGreater(high_score, full_score, f"Carre[{high_hand}] > Full[Ace]]")
            self.assertGreater(high_score, brelan_score, f"Carre[{high_hand}] > Brelan[Ace]")
            self.assertGreater(high_score, double_pair_score, f"Carre[{high_hand}] > Pair[Ace]")
            self.assertGreater(high_score, pair_score, f"Carre[{high_hand}] > Pair[Ace]")
            self.assertGreater(high_score, single_score, f"Carre[{high_hand}] > Ace")

    # Specifics
    def testFulls(self):
        # AssertionError: 24060 not greater than 24060 :
        # Full[Three of ♥|Three of ♣|Three of ♠|Four of ♥|Four of ♣]
        # > Full[Two of ♥|Two of ♣|Two of ♠|Four of ♥|Four of ♣]]

        full1 = Hand(cards=[Card(value=v) for v in [
            Value.Three,
            Value.Three,
            Value.Three,
            Value.Four,
            Value.Four,
        ]])
        full2 = Hand(cards=[Card(value=v) for v in [
            Value.Two,
            Value.Two,
            Value.Two,
            Value.Four,
            Value.Four,
        ]])
        self.assertGreater(full1.value(), full2.value(), "Full1 > full2 (threes >> twos)")