##############################################################################
# ScoDoc
# Copyright (c) 1999 - 2023 Emmanuel Viennet.  All rights reserved.
# See LICENSE
##############################################################################

"""Stockage des décisions de jury
"""
import pandas as pd

from app import db
from app.models import FormSemestre, Identite, ScolarFormSemestreValidation, UniteEns
from app.comp.res_cache import ResultatsCache
from app.scodoc import sco_cache
from app.scodoc import sco_codes_parcours


class ValidationsSemestre(ResultatsCache):
    """Les décisions de jury pour un semestre"""

    _cached_attrs = (
        "decisions_jury",
        "decisions_jury_ues",
        "ue_capitalisees",
    )

    def __init__(self, formsemestre: FormSemestre):
        super().__init__(formsemestre, sco_cache.ValidationsSemestreCache)

        self.decisions_jury = {}
        """Décisions prises dans ce semestre: 
        { etudid : { 'code' : None|ATT|..., 'assidu' : 0|1 }}"""
        self.decisions_jury_ues = {}
        """Décisions sur des UEs dans ce semestre:
        { etudid : { ue_id : { 'code' : Note|ADM|CMP, 'event_date': "d/m/y", "ects" : x}}}
        """
        self.ue_capitalisees: pd.DataFrame = None
        """DataFrame, index etudid
        formsemestre_id : origine de l'UE capitalisée
        is_external : vrai si validation effectuée dans un semestre extérieur
        ue_id : dans le semestre origine (pas toujours de la même formation)
        ue_code : code de l'UE
        moy_ue : note enregistrée
        event_date : date de la validation (jury)"""

        if not self.load_cached():
            self.compute()
            self.store()

    def compute(self):
        "Charge les résultats de jury et UEs capitalisées"
        self.ue_capitalisees = formsemestre_get_ue_capitalisees(self.formsemestre)
        self.comp_decisions_jury()

    def comp_decisions_jury(self):
        """Cherche les decisions du jury pour le semestre (pas les RCUE).
        Calcule les attributs:
        decisions_jury = { etudid : { 'code' : None|ATT|..., 'assidu' : 0|1 }}
        decision_jury_ues={ etudid :
            { ue_id : { 'code' : Note|ADM|CMP, 'event_date' : "d/m/y", 'ects' : x }}
        }
        Si la décision n'a pas été prise, la clé etudid n'est pas présente.
        Si l'étudiant est défaillant, pas de décisions d'UE.
        """
        # repris de NotesTable.comp_decisions_jury pour la compatibilité
        decisions_jury_q = ScolarFormSemestreValidation.query.filter_by(
            formsemestre_id=self.formsemestre.id
        )
        decisions_jury = {}
        for decision in decisions_jury_q.filter(
            db.text("ue_id is NULL")  # slt dec. sem.
        ):
            decisions_jury[decision.etudid] = {
                "code": decision.code,
                "assidu": decision.assidu,
                "compense_formsemestre_id": decision.compense_formsemestre_id,
                "event_date": decision.event_date.strftime("%d/%m/%Y"),
            }
        self.decisions_jury = decisions_jury

        # UEs: { etudid : { ue_id : {"code":, "ects":, "event_date":} }}
        decisions_jury_ues = {}
        # Parcours les décisions d'UE:
        for decision in (
            decisions_jury_q.filter(db.text("ue_id is not NULL"))
            .join(UniteEns)
            .order_by(UniteEns.numero)
        ):
            if decision.etudid not in decisions_jury_ues:
                decisions_jury_ues[decision.etudid] = {}
            # Calcul des ECTS associés à cette UE:
            if sco_codes_parcours.code_ue_validant(decision.code) and decision.ue:
                ects = decision.ue.ects or 0.0  # 0 if None
            else:
                ects = 0.0

            decisions_jury_ues[decision.etudid][decision.ue.id] = {
                "code": decision.code,
                "ects": ects,  # 0. si UE non validée
                "event_date": decision.event_date.strftime("%d/%m/%Y"),
            }

        self.decisions_jury_ues = decisions_jury_ues

    def has_decision(self, etud: Identite) -> bool:
        """Vrai si etud a au moins une décision enregistrée depuis
        ce semestre (quelle qu'elle soit)
        """
        return (etud.id in self.decisions_jury_ues) or (etud.id in self.decisions_jury)


def formsemestre_get_ue_capitalisees(formsemestre: FormSemestre) -> pd.DataFrame:
    """Liste des UE capitalisées (ADM) utilisables dans ce formsemestre

    Recherche dans les semestres des formations de même code, avec le même semestre_id
    et une date de début antérieure que celle du formsemestre.
    Prend aussi les UE externes validées.

    Attention: fonction très coûteuse, cacher le résultat.

    Résultat: DataFrame avec
        etudid (index)
        formsemestre_id : origine de l'UE capitalisée
        is_external : vrai si validation effectuée dans un semestre extérieur
        ue_id :  dans le semestre origine (pas toujours de la même formation)
        ue_code : code de l'UE
        moy_ue :
        event_date :
    } ]
    """

    # Note: pour récupérer aussi les UE validées en CMp ou ADJ, changer une ligne
    #   and ( SFV.code = 'ADM' or SFV.code = 'ADJ' or SFV.code = 'CMP' )

    query = """
    SELECT DISTINCT SFV.*, ue.ue_code
    FROM 
    notes_ue ue, 
    notes_formations nf,
    notes_formations nf2, 
    scolar_formsemestre_validation SFV, 
    notes_formsemestre sem,
    notes_formsemestre_inscription ins

    WHERE ue.formation_id = nf.id
    and nf.formation_code = nf2.formation_code
    and nf2.id=%(formation_id)s
    and ins.etudid = SFV.etudid
    and ins.formsemestre_id = %(formsemestre_id)s

    and SFV.ue_id = ue.id
    and SFV.code = 'ADM'

    and ( (sem.id = SFV.formsemestre_id
        and sem.date_debut < %(date_debut)s
        and sem.semestre_id = %(semestre_id)s )
        or (
            ((SFV.formsemestre_id is NULL) OR (SFV.is_external)) -- les UE externes ou "anterieures"
            AND (SFV.semestre_id is NULL OR SFV.semestre_id=%(semestre_id)s)
           ) )
    """
    params = {
        "formation_id": formsemestre.formation.id,
        "formsemestre_id": formsemestre.id,
        "semestre_id": formsemestre.semestre_id,
        "date_debut": formsemestre.date_debut,
    }

    df = pd.read_sql_query(query, db.engine, params=params, index_col="etudid")
    return df