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

"""Génération bulletin BUT
"""

import datetime
from flask import url_for, g

from app.scodoc import sco_utils as scu
from app.scodoc import sco_bulletins_json
from app.scodoc import sco_preferences
from app.scodoc.sco_utils import fmt_note
from app.comp.res_but import ResultatsSemestreBUT


class BulletinBUT(ResultatsSemestreBUT):
    """Génération du bulletin BUT.
    Cette classe génère des dictionnaires avec toutes les informations
    du bulletin, qui sont immédiatement traduisibles en JSON.
    """

    def etud_ue_mod_results(self, etud, ue, modimpls) -> dict:
        "dict synthèse résultats dans l'UE pour les modules indiqués"
        d = {}
        etud_idx = self.etud_index[etud.id]
        ue_idx = self.modimpl_coefs_df.index.get_loc(ue.id)
        etud_moy_module = self.sem_cube[etud_idx]  # module x UE
        for modimpl in modimpls:
            coef = self.modimpl_coefs_df[modimpl.id][ue.id]
            if coef > 0:
                d[modimpl.module.code] = {
                    "id": modimpl.id,
                    "coef": coef,
                    "moyenne": fmt_note(
                        etud_moy_module[
                            self.modimpl_coefs_df.columns.get_loc(modimpl.id)
                        ][ue_idx]
                    ),
                }
        return d

    def etud_ue_results(self, etud, ue):
        "dict synthèse résultats UE"
        d = {
            "id": ue.id,
            "numero": ue.numero,
            "ECTS": {
                "acquis": 0,  # XXX TODO voir jury
                "total": ue.ects,
            },
            "competence": None,  # XXX TODO lien avec référentiel
            "moyenne": {
                "value": fmt_note(self.etud_moy_ue[ue.id][etud.id]),
                "min": fmt_note(self.etud_moy_ue[ue.id].min()),
                "max": fmt_note(self.etud_moy_ue[ue.id].max()),
                "moy": fmt_note(self.etud_moy_ue[ue.id].mean()),
            },
            "bonus": None,  # XXX TODO
            "malus": None,  # XXX TODO voir ce qui est ici
            "capitalise": None,  # "AAAA-MM-JJ" TODO
            "ressources": self.etud_ue_mod_results(etud, ue, self.ressources),
            "saes": self.etud_ue_mod_results(etud, ue, self.saes),
        }
        return d

    def etud_mods_results(self, etud, modimpls) -> dict:
        """dict synthèse résultats des modules indiqués,
        avec évaluations de chacun."""
        d = {}
        # etud_idx = self.etud_index[etud.id]
        for modimpl in modimpls:
            # mod_idx = self.modimpl_coefs_df.columns.get_loc(mi.id)
            # # moyennes indicatives (moyennes de moyennes d'UE)
            # try:
            #     moyennes_etuds = np.nan_to_num(
            #         np.nanmean(self.sem_cube[:, mod_idx, :], axis=1),
            #         copy=False,
            #     )
            # except RuntimeWarning:  # all nans in np.nanmean (sur certains etuds sans notes valides)
            #     pass
            # try:
            #     moy_indicative_mod = np.nanmean(self.sem_cube[etud_idx, mod_idx])
            # except RuntimeWarning:  # all nans in np.nanmean
            #     pass
            modimpl_results = self.modimpls_results[modimpl.id]
            d[modimpl.module.code] = {
                "id": modimpl.id,
                "titre": modimpl.module.titre,
                "code_apogee": modimpl.module.code_apogee,
                "url": url_for(
                    "notes.moduleimpl_status",
                    scodoc_dept=g.scodoc_dept,
                    moduleimpl_id=modimpl.id,
                ),
                "moyenne": {
                    # # moyenne indicative de module: moyenne des UE, ignorant celles sans notes (nan)
                    # "value": fmt_note(moy_indicative_mod),
                    # "min": fmt_note(moyennes_etuds.min()),
                    # "max": fmt_note(moyennes_etuds.max()),
                    # "moy": fmt_note(moyennes_etuds.mean()),
                },
                "evaluations": [
                    self.etud_eval_results(etud, e)
                    for e in modimpl.evaluations
                    if e.visibulletin
                    and modimpl_results.evaluations_etat[e.id].is_complete
                ],
            }
        return d

    def etud_eval_results(self, etud, e) -> dict:
        "dict resultats d'un étudiant à une évaluation"
        # eval_notes est une pd.Series avec toutes les notes des étudiants inscrits
        eval_notes = self.modimpls_results[e.moduleimpl_id].evals_notes[e.id]
        notes_ok = eval_notes.where(eval_notes > scu.NOTES_ABSENCE).dropna()
        d = {
            "id": e.id,
            "description": e.description,
            "date": e.jour.isoformat() if e.jour else None,
            "heure_debut": e.heure_debut.strftime("%H:%M") if e.heure_debut else None,
            "heure_fin": e.heure_fin.strftime("%H:%M") if e.heure_debut else None,
            "coef": e.coefficient,
            "poids": {p.ue.acronyme: p.poids for p in e.ue_poids},
            "note": {
                "value": fmt_note(
                    eval_notes[etud.id],
                    note_max=e.note_max,
                ),
                "min": fmt_note(notes_ok.min()),
                "max": fmt_note(notes_ok.max()),
                "moy": fmt_note(notes_ok.mean()),
            },
            "url": url_for(
                "notes.evaluation_listenotes",
                scodoc_dept=g.scodoc_dept,
                evaluation_id=e.id,
            ),
        }
        return d

    def bulletin_etud(self, etud, formsemestre) -> dict:
        """Le bulletin de l'étudiant dans ce semestre"""
        etat_inscription = etud.etat_inscription(formsemestre.id)
        d = {
            "version": "0",
            "type": "BUT",
            "date": datetime.datetime.utcnow().isoformat() + "Z",
            "etudiant": etud.to_dict_bul(),
            "formation": {
                "id": formsemestre.formation.id,
                "acronyme": formsemestre.formation.acronyme,
                "titre_officiel": formsemestre.formation.titre_officiel,
                "titre": formsemestre.formation.titre,
            },
            "formsemestre_id": formsemestre.id,
            "etat_inscription": etat_inscription,
            "options": sco_preferences.bulletin_option_affichage(formsemestre.id),
        }
        semestre_infos = {
            "etapes": [str(x.etape_apo) for x in formsemestre.etapes if x.etape_apo],
            "date_debut": formsemestre.date_debut.isoformat(),
            "date_fin": formsemestre.date_fin.isoformat(),
            "annee_universitaire": self.formsemestre.annee_scolaire_str(),
            "inscription": "TODO-MM-JJ",  # XXX TODO
            "numero": formsemestre.semestre_id,
            "groupes": [],  # XXX TODO
            "absences": {  # XXX TODO
                "injustifie": -1,
                "total": -1,
            },
        }
        semestre_infos.update(
            sco_bulletins_json.dict_decision_jury(etud.id, formsemestre.id)
        )
        if etat_inscription == scu.INSCRIT:
            semestre_infos.update(
                {
                    "notes": {  # moyenne des moyennes générales du semestre
                        "value": fmt_note(self.etud_moy_gen[etud.id]),
                        "min": fmt_note(self.etud_moy_gen.min()),
                        "moy": fmt_note(self.etud_moy_gen.mean()),
                        "max": fmt_note(self.etud_moy_gen.max()),
                    },
                    "rang": {  # classement wrt moyenne général, indicatif
                        "value": self.etud_moy_gen_ranks[etud.id],
                        "total": len(self.etuds),
                    },
                },
            )
            d.update(
                {
                    "ressources": self.etud_mods_results(etud, self.ressources),
                    "saes": self.etud_mods_results(etud, self.saes),
                    "ues": {
                        ue.acronyme: self.etud_ue_results(etud, ue) for ue in self.ues
                    },
                    "semestre": semestre_infos,
                },
            )
        else:
            semestre_infos.update(
                {
                    "notes": {
                        "value": "DEM",
                        "min": "",
                        "moy": "",
                        "max": "",
                    },
                    "rang": {"value": "DEM", "total": len(self.etuds)},
                }
            )
            d.update(
                {
                    "semestre": semestre_infos,
                    "ressources": {},
                    "saes": {},
                    "ues": {},
                }
            )

        return d
