##############################################################################
# ScoDoc
# Copyright (c) 1999 - 2022 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, ScolarFormSemestreValidation
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' }}}
        """
        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 UE).
        Calcule les attributs:
        decisions_jury = { etudid : { 'code' : None|ATT|..., 'assidu' : 0|1 }}
        decision_jury_ues={ etudid : { ue_id : { 'code' : Note|ADM|CMP, 'event_date' }}}
        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(
            ScolarFormSemestreValidation.ue_id == None  # 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:
        decisions_jury_ues = {}
        for decision in decisions_jury_q.filter(
            ScolarFormSemestreValidation.ue_id != None  # slt dec. sem.
        ):
            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):
                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 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 :
    } ]
    """
    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