from models.cards import Card
from game_logic.game_states import GameState
from services.rpi import RPIService

N_PLAYS = 8
N_PLAYERS = 4
MAX_ROUND_POINTS = 60
DELTA_POINTS = int(MAX_ROUND_POINTS / 2)
POINTS_REQUIRED = 121

CARD_ORDER = ["7", "8", "9", "Jack", "Queen", "King", "Ace", "10"]

CARD_POINTS = {
    "7": 0,
    "8": 0,
    "9": 0,
    "Jack": 1,
    "Queen": 2,
    "King": 3,
    "Ace": 4,
    "10": 5
}

PLAYERS = {
    0: "North",
    1: "East",
    2: "South",
    3: "West"
}

TEAMS = {
    0: "NS",
    1: "EW",
    2: "NS",
    3: "EW",
}

class Play:
    def __init__(self, trump_symbol: str, starting_player: int):
        self.trump_symbol = trump_symbol
        self.player_turns = [(i + starting_player) % N_PLAYERS for i in range(N_PLAYERS)]
        self.play_cards: list[Card] = []

    def add_card(self, cards: list[Card]):
        for card in cards:
            card_is_not_played = card not in self.play_cards
            value_is_valid = card.value in CARD_ORDER

            if card_is_not_played and value_is_valid:
                self.play_cards.append(card)
                if len(self.play_cards) == N_PLAYERS:
                    return self.get_points()
                break

    def get_points(self):
        leading_symbol = self.play_cards[0].symbol
        trump_cards = [card for card in self.play_cards if card.symbol == self.trump_symbol]
        leading_cards = [card for card in self.play_cards if card.symbol == leading_symbol]

        if trump_cards:
            winner_card = max(trump_cards, key=lambda c: CARD_ORDER.index(c.value))
        else:
            winner_card = max(leading_cards, key=lambda c: CARD_ORDER.index(c.value))

        winner_index = self.play_cards.index(winner_card)
        winner_player = self.player_turns[winner_index]
        
        total_points = sum(CARD_POINTS[card.value] for card in self.play_cards)
        
        return winner_player, total_points

class Round:
    def __init__(self, trump_symbol: str, multiplier: int, starting_player: int):
        self.trump_symbol = trump_symbol
        self.multiplier = multiplier
        self.plays: list[Play] = []
        self.play = Play(trump_symbol, starting_player)
        self.score = {"NS": 0, "EW": 0}

    def play_card(self, cards: list[Card]):
        result = self.play.add_card(cards)
        if result:
            return self.end_play(result)
    
    def end_play(self, result: tuple[int, int]):
        winning_player, points = result
        self.score[TEAMS[winning_player]] += points
        self.plays.append(self.play)
        self.play = Play(self.trump_symbol, winning_player)
        if len(self.plays) >= N_PLAYS:
            return self.score 

class GameManager:
    def __init__(self, rpi_service: RPIService):
        self.game_state = GameState.ROUND_CREATING
        self.round: Round = None
        self.dealer: int = 0
        self.score = {"NS": 0, "EW": 0}
        self.rpi_service = rpi_service
        self.rpi_service.send_message("connected", "pick trump card")

    def start_round(self, trump_symbol: str, multiplier: int):
        starting_player = (self.dealer + 1) % N_PLAYERS
        self.round = Round(trump_symbol, multiplier, starting_player)
        self.game_state = GameState.ROUND_PLAYING
        self.rpi_service.send_message(f"trump: {self.round.trump_symbol}", f"player: {PLAYERS[starting_player]}")

    def play_card(self, cards: list[Card]):
        score = self.round.play_card(cards)
        if score:
            self.update_score(score)
            if self.game_state != GameState.ROUND_PLAYING:
                return
            
        player_turns = self.round.play.player_turns
        cards_played = self.round.play.play_cards
        turn_player = player_turns[len(cards_played)]
        player_name = PLAYERS[turn_player]

        self.rpi_service.send_message(f"trump: {self.round.trump_symbol}", f"player: {player_name}")

    def get_bonus(self):
        bonus = self.round.multiplier
        if self.round.trump_symbol == "None":
            bonus *= 2
        return bonus

    def update_score(self, score: dict[str, int]):
        delta_ns = score["NS"] - DELTA_POINTS
        delta_ew = score["EW"] - DELTA_POINTS
        round_is_valid = (delta_ns + delta_ew) == 0

        if round_is_valid:
            bonus = self.get_bonus()
            if delta_ns == -DELTA_POINTS:
                self.score["EW"] += MAX_ROUND_POINTS * bonus
            elif delta_ns < 0:
                self.score["EW"] += delta_ew * bonus
            elif delta_ns == 0:
                pass
            elif delta_ns < 30:
                self.score["NS"] += delta_ns * bonus
            elif delta_ns == 30:
                self.score["NS"] += MAX_ROUND_POINTS * bonus

            self.game_state = GameState.ROUND_ENDED
            self.rpi_service.send_message(f"round NS: {score["NS"]}", f"round EW: {score["EW"]}")
        else:
            self.game_state = GameState.ROUND_ERROR
            self.rpi_service.send_message("ERROR", "score sum not 60")

    def solve_round_error(self, points: int, team: str):
        bonus = self.get_bonus()
        if points == 30:
            self.score[team] += MAX_ROUND_POINTS * bonus
        else:
            self.score[team] += points * bonus

        self.end_round()

    def end_round(self):
        self.dealer += (self.dealer + 1) % N_PLAYERS
        if self.score["NS"] >= POINTS_REQUIRED:
            self.game_state = GameState.NS_WINS_GAME
            self.rpi_service.send_message(f"WINNER NS: {self.score["NS"]}", f"       EW: {self.score["EW"]}")
        elif self.score["EW"] >= POINTS_REQUIRED:
            self.game_state = GameState.EW_WINS_GAME
            self.rpi_service.send_message(f"       NS: {self.score["NS"]}", f"WINNER EW: {self.score["EW"]}")
        else:
            self.game_state = GameState.ROUND_CREATING
            self.rpi_service.send_message(f"NS: {self.score["NS"]} / EW: {self.score["EW"]}", f"dealer: {PLAYERS[self.dealer]}")