from collections import Counter
from typing import List

from pydantic.main import BaseModel

from server.encoding import encode_color, encode_value
from server.model.card import Card
from server.model.color import Color
from server.model.value import Value


class Hand(BaseModel):
    cards: List[Card] = []

    class Config:
        json_encoders = {
            Value: encode_value,
            Color: encode_color,
        }

    def __contains__(self, item: Card):
        return item in self.cards

    def __len__(self) -> int:
        return len(self.cards)

    def __getitem__(self, item):
        return self.cards[item]

    def __repr__(self):
        return "|".join([str(c) for c in self.cards])

    def give(self, other: Card):
        """
        Adds another card to this hand.

        :raises ValueError if the card is already in this hand.
        """
        if (other.value, other.color) in [(c.value, c.color) for c in self.cards]:
            raise ValueError(f"TRICHEUR! {other} already in this hand: {self.cards}!")
        self.cards.append(other)

    def value(self):
        """ Scores this hand according to the Poker Menteur rules."""
        counter = Counter([c.value for c in self.cards])

        has_pair = False
        has_double_pair = False
        has_brelan = False
        has_full = False
        has_carre = False

        highest_card = None
        pair = None
        double_pair = None
        brelan = None
        carre = None

        for element, count in counter.items():
            element_cards = [c for c in self.cards if c.value == element]
            card = element_cards[0]  # Note we take a random color
            highest_card = max(highest_card, card) if highest_card else card

            if count == 2:
                if has_pair:
                    has_double_pair = True
                    double_pair = max(pair, card)
                    pair = min(pair, card)
                else:
                    has_pair = True
                    pair = max(pair, card) if pair else card
            if count == 3:
                has_brelan = True
                brelan = max(brelan, card) if brelan else card
            if has_brelan and has_pair:
                has_full = True
            if count == 4:
                has_carre = True
                carre = max(carre, card) if carre else card

        analysis_log = " | ".join([
            f"ANALYSIS",
            f"Carre[{carre}]" if has_carre else "no carre",
            f"Full[{brelan}|{pair}]" if has_full else "no full",
            f"Brelan[{brelan}]" if has_brelan else "no carre",
            f"Double paire[{double_pair}|{pair}]" if has_double_pair else "no Dpaire",
            f"Paire[{pair}]" if has_pair else "no paire",
            f"Card[{highest_card}]"
        ])
        # Finished counting, let's return scores
        if has_carre:
            score = (20 ** 5) * carre.score()
        elif has_full:
            score = (20 ** 4) * brelan.score() + 20 * pair.score()
        elif has_brelan:
            score = (20 ** 3) * brelan.score()
        elif has_double_pair:
            score = (20 ** 2) * double_pair.score() + pair.score()
        elif has_pair:
            score = 20 * pair.score()
        else:
            score = highest_card.score()

        analysis_log += f"\t-> score=\t{score}"
        # print(analysis_log)
        return score

    @property
    def is_menteur(self) -> bool:
        return len(self.cards) == 0

    @property
    def is_carre_as(self) -> bool:
        return len(self.cards) == 4 and all([c.value == Value.Ace for c in self.cards])
