from abc import abstractmethod, ABC
from typing import Optional

from pydantic.main import BaseModel

from server.model.animals import random_animal_name
from server.model.card import Card
from server.model.hand import Hand
from server.model.hands import random_option


class Announce(BaseModel):
    bet: Optional[Hand] = None

    @property
    def menteur(self):
        return not self.bet


class Player(ABC):
    def __init__(self, name: str = None):
        if not name:
            name = random_animal_name()
        self.name: str = name
        self.hand: Hand = Hand()

    def __str__(self):
        return f"Player {self.name}"

    def __repr__(self):
        return str(self)

    def give(self, card: Card):
        self.hand.give(card)

    def clear(self):
        self.hand.cards.clear()

    @abstractmethod
    async def announce(self, current_bet: Optional[Hand]) -> Announce:
        """
        Announces a bet or Menteur, based on the current bet.
        :param current_bet:
        :return:
        """
        return Announce()


class NaivePlayer(Player, ABC):
    """ A naive player that only trusts what they sees:
    bets his hand, or menteur if his hand is lower. """

    def __str__(self):
        return "Naive " + super().__str__()

    async def announce(self, current_bet: Optional[Hand]) -> Announce:
        if not current_bet or self.hand.value() > current_bet.value():
            return Announce(bet=self.hand)
        else:
            return Announce()


class RandomPlayer(Player, ABC):
    """ A weird player that never says menteur, always betting a random option. """

    def __str__(self):
        return "Random " + super().__str__()

    async def announce(self, current_bet: Optional[Hand]) -> Announce:
        if current_bet and not current_bet.is_menteur and current_bet.is_carre_as:
            return Announce()
        else:
            return Announce(bet=random_option())


class MenteurPlayer(Player, ABC):
    """ A crazy player that always says menteur. """

    def __str__(self):
        return "Menteur " + super().__str__()

    async def announce(self, current_bet: Optional[Hand]) -> Announce:
        hand = random_option()
        return Announce(bet=hand)
