import app.pe.pe_comp as pe_comp
import app.pe.pe_affichage as pe_affichage

from app.models import FormSemestre
from app.pe.pe_etudiant import EtudiantsJuryPE, get_dernier_semestre_en_date


class Trajectoire:
    """Modélise, pour un aggrégat visé (par ex: 'S2', '3S', '2A')
    et un ensemble d'étudiants donnés,
    la combinaison des formsemestres des étudiants amenant à un semestre
    terminal visé.

    Si l'aggrégat est un semestre de type Si, elle stocke le (ou les)
    formsemestres de numéro i qu'ont suivis l'étudiant pour atteindre le Si
    (en général 1 si personnes n'a redoublé, mais 2 s'il y a des redoublants)

    Pour des aggrégats de type iS ou iA (par ex, 3A=S1+S2+S3), elle identifie
    les semestres que les étudiants ont suivis pour les amener jusqu'au semestre
    terminal de la trajectoire (par ex: ici un S3).
    Ces semestres peuvent être :
    * des S1+S2+S1+S2+S3 si redoublement de la 1ère année
    * des S1+S2+(année de césure)+S3 si césure, ...
    """

    def __init__(self, nom_aggregat: str, semestre_final: FormSemestre):
        """Modélise un ensemble de formsemestres d'étudiants
        amenant à un semestre terminal

        Args:
            nom_aggregat: Un nom d'aggrégat (par ex: '5S')
            semestre_final: Le semestre final de l'aggrégat
        """
        self.nom = nom_aggregat
        self.semestre_final = semestre_final
        self.trajectoire_id = (nom_aggregat, semestre_final.formsemestre_id)

        """Les semestres à aggréger"""
        self.semestres_aggreges = {}

    def add_semestres_a_aggreger(self, semestres: dict[int:FormSemestre]):
        """Ajoute des semestres au semestre à aggréger

        Args:
            semestres: Dictionnaire ``{fid: FormSemestre(fid)} à ajouter``
        """
        self.semestres_aggreges = self.semestres_aggreges | semestres

    def get_repr(self, verbose=True) -> str:
        """Représentation textuelle d'une trajectoire
        basée sur ses semestres aggrégés"""

        noms = []
        for fid in self.semestres_aggreges:
            semestre = self.semestres_aggreges[fid]
            noms.append(f"S{semestre.semestre_id}({fid})")
        noms = sorted(noms)
        repr = f"{self.nom} ({self.semestre_final.formsemestre_id}) {self.semestre_final.date_fin.year}"
        if verbose and noms:
            repr += " - " + "+".join(noms)
        return repr


class TrajectoiresJuryPE:
    """Centralise toutes les trajectoires du jury PE"""

    def __init__(self, annee_diplome: int):
        """
        Args:
            annee_diplome: L'année de diplomation
        """

        self.annee_diplome = annee_diplome
        """Toutes les trajectoires possibles"""
        self.trajectoires: dict[tuple:Trajectoire] = {}
        """Quelle trajectoires pour quel étudiant :
        dictionnaire {etudid: {nom_aggregat: Trajectoire}}"""
        self.suivi: dict[int:str] = {}

    def cree_trajectoires(self, etudiants: EtudiantsJuryPE):
        """Créé toutes les trajectoires, au regard du cursus des étudiants
        analysés + les mémorise dans les données de l'étudiant
        """

        for nom_aggregat in pe_comp.TOUS_LES_SEMESTRES + pe_comp.TOUS_LES_AGGREGATS:
            """L'aggrégat considéré (par ex: 3S=S1+S2+S3), son nom de son semestre terminal (par ex: S3) et son numéro (par ex: 3)"""
            noms_semestre_de_aggregat = pe_comp.PARCOURS[nom_aggregat]["aggregat"]
            nom_semestre_terminal = noms_semestre_de_aggregat[-1]

            for etudid in etudiants.cursus:
                if etudid not in self.suivi:
                    self.suivi[etudid] = {
                        aggregat: None
                        for aggregat in pe_comp.TOUS_LES_SEMESTRES
                        + pe_comp.TOUS_LES_AGGREGATS
                    }

                """Le formsemestre terminal (dernier en date) associé au 
                semestre marquant la fin de l'aggrégat 
                (par ex: son dernier S3 en date)"""
                semestres = etudiants.cursus[etudid][nom_semestre_terminal]
                if semestres:
                    formsemestre_final = get_dernier_semestre_en_date(semestres)

                    """Ajout ou récupération de la trajectoire"""
                    trajectoire_id = (nom_aggregat, formsemestre_final.formsemestre_id)
                    if trajectoire_id not in self.trajectoires:
                        trajectoire = Trajectoire(nom_aggregat, formsemestre_final)
                        self.trajectoires[trajectoire_id] = trajectoire
                    else:
                        trajectoire = self.trajectoires[trajectoire_id]

                    """La liste des semestres de l'étudiant à prendre en compte
                    pour cette trajectoire"""
                    semestres_a_aggreger = etudiants.get_trajectoire(
                        etudid, formsemestre_final, nom_aggregat
                    )

                    """Ajout des semestres à la trajectoire"""
                    trajectoire.add_semestres_a_aggreger(semestres_a_aggreger)

                    """Mémoire la trajectoire suivie par l'étudiant"""
                    self.suivi[etudid][nom_aggregat] = trajectoire

            """Vérifications"""
            # dernier_semestre_aggregat = get_dernier_semestre_en_date(semestres_aggreges)
            # assert dernier_semestre_aggregat == formsemestre_terminal


def get_trajectoires_etudid(trajectoires, etudid):
    """Fonction pour débuggage: renvoie la liste des trajectoires_id des
    trajectoires suivies par un étudiant
    """
    if etudid not in trajectoires.suivi:
        pe_affichage.pe_print(f"{etudid} fait-il bien partie du jury ?")

    liste = []
    for aggregat in pe_comp.TOUS_LES_PARCOURS:
        trajet = trajectoires.suivi[etudid][aggregat]
        if trajet:
            liste.append(trajet.trajectoire_id)
    return liste


def get_semestres_a_aggreger(self, aggregat: str, formsemestre_id_terminal: int):
    """Pour un nom d'aggrégat donné (par ex: 'S3') et un semestre terminal cible
    identifié par son formsemestre_id (par ex: 'S3 2022-2023'),
    renvoie l'ensemble des semestres à prendre en compte dans
    l'aggrégat sous la forme d'un dictionnaire {fid: FormSemestre(fid)}.

    Fusionne les cursus individuels des étudiants, dont le cursus correspond
    à l'aggrégat visé.

    Args:
        aggregat: Un aggrégat (par ex. 1A, 2A, 3S, 6S)
        formsemestre_id_terminal: L'identifiant du formsemestre terminal de l'aggrégat, devant correspondre au
                                  dernier semestre de l'aggrégat
    """
    noms_semestres_aggreges = pe_comp.PARCOURS[aggregat]["aggregat"]

    formsemestres = {}
    for etudid in self.cursus:
        cursus_etudiant = self.cursus[etudid][aggregat]
        if formsemestre_id_terminal in cursus_etudiant:
            formsemestres_etudiant = cursus_etudiant[formsemestre_id_terminal]
            formsemestres = formsemestres | formsemestres_etudiant
    return formsemestres