from joueur import Joueur
from jeu import Jeu
from pioche import Pioche
from personnage import Personnage, creer_personnages
from quartier import Quartier, creer_quartiers
from ui import UI
import random as rnd


class Partie():
    """
    Instance d'une partie à deux joueurs pour le jeu Citadelles.
    
    Cette classe gère le déroulement complet d'une partie, incluant :
    - La création et la gestion des joueurs
    - La distribution des quartiers et personnages
    - Le déroulement des tours
    - Les conditions de fin de partie
    
    Attributes:
        _joueurs (list[Joueur]): Liste des deux joueurs
        _est_couronne (int): Index du joueur qui a la couronne (0 ou 1)
        _personnages (list[Personnage]): Liste de tous les personnages disponibles
        _quartiers (Pioche): Pioche contenant tous les quartiers disponibles
    """

    def __init__(self, joueur1: str, joueur2 : str):
        """
        Initialise une nouvelle partie avec deux joueurs.
        
        Args:
            joueur1 (str): Nom du premier joueur
            joueur2 (str): Nom du second joueur
        
        Effects:
            - Crée deux instances de Joueur
            - Attribue aléatoirement la couronne à un joueur
            - Crée la liste complète des personnages
            - Initialise la pioche de quartiers
        """
        self._joueurs = [Joueur(joueur1), Joueur(joueur2)]
        self._est_couronne = rnd.choice([0, 1])
        self._personnages = creer_personnages()
        self._quartiers = Pioche(creer_quartiers())
    
    def lancer_partie(self):
        """
        Lance et gère le déroulement complet de la partie.
        
        La partie se déroule en plusieurs étapes :
        1. Distribution initiale des quartiers (4 par joueur)
        2. Distribution initiale de l'or (2 pièces par joueur)
        3. Boucle des tours :
           - Distribution des personnages
           - Déroulement du tour (actions de chaque personnage)
        4. Vérification des conditions d'arrêt
        5. Comptage des points en fin de partie
        
        La partie s'arrête quand un joueur a construit 8 quartiers.
        """
        # TODO: compléter cette fonction
    UI.info('(À implémenter)')
    pass



    def _distribuer_quartiers_initiaux(self):
        """
        Distribue les quartiers initiaux aux joueurs.
        
        Chaque joueur reçoit 4 quartiers piochés aléatoirement dans la pioche.
        L'ordre de distribution dépend de qui a la couronne :
        - Si joueur 0 a la couronne : joueur 0 commence
        - Si joueur 1 a la couronne : joueur 1 commence
        
        Effects:
            - Mélange la pioche de quartiers
            - Distribue 4 cartes à chaque joueur
            - Les cartes sont ajoutées au jeu (main) de chaque joueur
        """
        UI.subtitle("Distribution des quartiers initiaux")
        self._quartiers.melanger()
        order = [0, 1] if self._est_couronne == 0 else [1, 0]
        for _ in range(0, 4):
            for j in order:
                joueur = self._joueurs[j]
                carte = self._quartiers.piocher()
                joueur._jeu.ajouter_carte(carte)

    def _distribuer_personnages(self):
        """
        Distribue les personnages aux joueurs pour un tour.
        
        Processus :
        1. Retire 2 personnages aléatoires de la liste complète
        2. Le joueur avec la couronne choisit en premier
        3. Chaque joueur choisit 2 personnages à tour de rôle
        4. L'ordre de choix : joueur1 -> joueur2 -> joueur2 -> joueur1
        
        Note: Ce mécanisme permet à chaque joueur d'avoir 2 personnages par tour.
        
        Effects:
            - Crée un jeu temporaire de personnages disponibles
            - Retire 2 personnages aléatoires
            - Chaque joueur choisit 2 personnages parmi ceux disponibles
        """
        UI.subtitle("Choix des personnages")
        # retirer deux cartes au hasard
        personnages = Jeu(list(self._personnages))
        personnages.retirer_carte(rnd.choice(personnages.get_cartes()))
        personnages.retirer_carte(rnd.choice(personnages.get_cartes()))
        joueur1 = self._joueurs[self._est_couronne]
        joueur2 = self._joueurs[1 - self._est_couronne]
        
        UI.info(f"{joueur1._nom} choisit en premier (a la couronne)")
        UI.pause(f"Appuyez sur Entrée pour continuer...")
        self._choisir_personnage(joueur1, personnages)
        UI.pause(f"C'est maintenant à {joueur2._nom} de choisir.\nAppuyez sur Entrée pour continuer...")
        self._choisir_personnage(joueur2, personnages)
        self._choisir_personnage(joueur2, personnages)
        UI.pause(f"{joueur1._nom} fait son dernier choix.\nAppuyez sur Entrée pour continuer...")
        self._choisir_personnage(joueur1, personnages)
        Partie._clear_screen()

    def _lancer_tour(self):
        """
        Lance un nouveau tour de jeu.
        
        Un tour consiste à faire jouer chaque personnage dans l'ordre.
        Pour chaque personnage :
        1. On vérifie quel joueur possède ce personnage
        2. Si un joueur le possède, il joue son tour avec ce personnage
        3. Si aucun joueur ne le possède, le personnage est hors jeu ce tour
        
        Effects:
            - Efface l'écran
            - Affiche le titre du nouveau tour
            - Pour chaque personnage, trouve le joueur correspondant et lance son tour
            - Affiche un séparateur après chaque tour de joueur
        """
        UI.clear()
        UI.title("🎲 NOUVEAU TOUR 🎲")
        joueur_courant = None
        for p in self._personnages : 
            UI.subtitle(f"Tour du personnage: {p._nom}")
            for j in range(0,2):
                if p in self._joueurs[j]._personnages:
                    joueur_courant = self._joueurs[j]
                    break;
            if joueur_courant != None:
                self._jouer_tour(joueur_courant, p, self._quartiers)
                UI.separator()
            else:
                UI.info(f"{p._nom} est hors jeu ce tour-ci.")
            joueur_courant = None


    def _conditions_arret(self) -> bool:
        """
        Vérifie si la partie doit s'arrêter.
        
        La partie s'arrête lorsqu'un joueur a construit 8 quartiers dans sa cité.
        
        Returns:
            bool: True si un joueur a 8 quartiers (partie terminée), False sinon
        """
        # TODO: compléter cette fonction
    UI.info('(À implémenter)')
    pass


    def _comptage_points(self):
        """
        Effectue le comptage des points en fin de partie.
        
        Cette méthode est appelée quand la condition d'arrêt est atteinte.
        Elle devrait calculer et afficher les points de chaque joueur
        en fonction des quartiers construits dans leur cité.
        
        Note: Actuellement non implémentée (à compléter).
        
        Effects:
            - Calcule les points de chaque joueur
            - Affiche le gagnant
        """
        # TODO: compléter cette fonction
    UI.info('(À implémenter)')
    pass


    def _choisir_personnage(self, joueur: Joueur, personnages: Jeu) -> bool:
        """
        Permet à un joueur de choisir un personnage parmi ceux disponibles.
        
        Args:
            joueur (Joueur): Le joueur qui doit choisir un personnage
            personnages (Jeu): Le jeu de personnages disponibles pour le choix
        
        Returns:
            bool: True si le joueur a choisi le Roi (ordre 4), False sinon
        
        Effects:
            - Efface l'écran pour une meilleure lisibilité
            - Affiche le tour du joueur
            - Affiche le menu des personnages disponibles
            - Ajoute le personnage choisi à la liste du joueur
            - Retire le personnage du jeu des personnages disponibles
            - Met à jour la couronne si le Roi a été choisi
        """
        UI.clear()
        UI.player_turn(f"{joueur._nom}")
        UI.subtitle(f"{joueur._nom}, choisissez un personnage")
        cartes = personnages.get_cartes()
        choix = UI.menu(cartes)
        carte = cartes[choix]
        personnages.retirer_carte(carte)
        joueur._personnages.append(carte)
        UI.success(f"Vous avez choisi: {carte._nom}")
        UI.separator()
        
        # Met à jour la couronne si le Roi a été choisi
        if carte._ordre == 4:
            self._est_couronne = 0 if joueur == self._joueurs[0] else 1
        
        return carte._ordre == 4  # c'est le roi

    def _jouer_tour(self, joueur: Joueur, personnage: Personnage, pioche: Pioche):
        """
        Gère le tour d'un joueur avec un personnage spécifique.
        
        Args:
            joueur (Joueur): Le joueur dont c'est le tour
            personnage (Personnage): Le personnage actuellement actif
            pioche (Pioche): La pioche de quartiers pour piocher
        
        Effects:
            - Affiche un récapitulatif de l'état du joueur
            - Execute les 3 actions du tour :
              1. Choix du revenu (or ou quartiers)
              2. Construction de quartier (optionnelle)
              3. Application du pouvoir spécial du personnage
        """
        # Afficher récapitulatif du joueur
        UI.subtitle(f"Tour de {joueur._nom}")
        UI.info(f"Pièces: {joueur._pieces} | Quartiers: {len(joueur._cite.get_quartiers())} | Cartes en main: {len(joueur._jeu.get_cartes())}")
        UI.separator()
        
        self._choisir_revenu(joueur, pioche)
        self._construire_quartier(joueur)
        self._appliquer_pouvoir(personnage)

    def _choisir_revenu(self, joueur: Joueur, pioche: Pioche):
        """
        Permet au joueur de choisir son revenu pour le tour.
        
        Args:
            joueur (Joueur): Le joueur qui choisit son revenu
            pioche (Pioche): La pioche de quartiers
        
        Effects:
            - Affiche le menu des options de revenu
            - Si choix = or : ajoute 2 pièces au joueur
            - Si choix = quartiers : pioche 2 quartiers, le joueur en garde 1
        """
        choix = UI.menu(
            ["Prendre deux pièces d'or", "Piocher deux quartiers (garder 1)"],
            "Choix du revenu"
        )
        if choix == 0:
            joueur.recevoir_or(2)
            UI.success(f"Vous recevez 2 pièces d'or (total: {joueur._pieces})")
        else:
            q1 = pioche.piocher()
            q2 = pioche.piocher()
            UI.subtitle("Choisissez un quartier à garder")
            choix_quartier = UI.menu([q1, q2])
            joueur._jeu.ajouter_carte(q1 if choix_quartier == 0 else q2)
            pioche.ajouter_carte(q2 if choix_quartier == 0 else q1)
            UI.success(f"Vous gardez: {q1._nom if choix_quartier == 0 else q2._nom}")

    def _construire_quartier(self, joueur: Joueur):
        """
        Permet au joueur de construire un quartier s'il en a les moyens.
        
        Args:
            joueur (Joueur): Le joueur qui peut construire un quartier
        
        Effects:
            - Filtre les quartiers constructibles (coût <= pièces du joueur)
            - Si aucun quartier constructible : affiche un message et retourne
            - Affiche la liste des quartiers constructibles
            - Demande si le joueur veut construire
            - Si oui, demande quel quartier construire et tente la construction
        """
        if not joueur._jeu.get_cartes():
            UI.info("Vous n'avez aucun quartier en main")
            return
        
        # Filtrer les quartiers que le joueur peut construire
        quartiers_constructibles = [
            q for q in joueur._jeu.get_cartes() 
            if q.get_cout() <= joueur._pieces
        ]
        
        if not quartiers_constructibles:
            UI.info("Vous n'avez pas assez de pièces pour construire un quartier")
            return
        
        # Afficher la liste des quartiers constructibles
        UI.show_cards(quartiers_constructibles, "Vos quartiers constructibles")
        
        if UI.yes_no_prompt("Voulez-vous construire un quartier ?"):
            choix = int(input(f"Choix du quartier [1-{len(quartiers_constructibles)}]: ")) - 1
            q = quartiers_constructibles[choix]
            # On a déjà filtré par coût, donc la construction devrait réussir
            if joueur.construire_quartier(q):
                UI.success(f"Quartier {q._nom} construit !")

    def _appliquer_pouvoir(self, personnage: Personnage):
        """
        Applique le pouvoir spécial d'un personnage.
        
        Args:
            personnage (Personnage): Le personnage dont on applique le pouvoir
        
        Effects:
            - Affiche le nom et la description du pouvoir
            - Note: À implémenter selon les règles spécifiques de chaque personnage
        """
        UI.warning(f"Pouvoir spécial de {personnage._nom}: {personnage._description}")
        UI.info("(À implémenter)")
        pass
