# -*- mode: python -*-
# -*- coding: utf-8 -*-

##############################################################################
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2022 Emmanuel Viennet.  All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#   Emmanuel Viennet      emmanuel.viennet@viennet.net
#
##############################################################################

"""Génération du bulletin en format JSON (beta, non completement testé)

"""
import datetime
import json

from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat
from app.models.formsemestre import FormSemestre
from app.models.etudiants import Identite

import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
from app.scodoc import sco_abs
from app.scodoc import sco_edit_ue
from app.scodoc import sco_evaluation_db
from app.scodoc import sco_formsemestre
from app.scodoc import sco_groups
from app.scodoc import sco_photos
from app.scodoc import sco_preferences
from app.scodoc import sco_etud

# -------- Bulletin en JSON


def make_json_formsemestre_bulletinetud(
    formsemestre_id: int,
    etudid: int,
    xml_with_decisions=False,
    version="long",
    force_publishing=False,  # force publication meme si semestre non publie sur "portail"
) -> str:
    """Renvoie bulletin en chaine JSON"""

    d = formsemestre_bulletinetud_published_dict(
        formsemestre_id,
        etudid,
        force_publishing=force_publishing,
        xml_with_decisions=xml_with_decisions,
        version=version,
    )

    return json.dumps(d, cls=scu.ScoDocJSONEncoder)


# (fonction séparée: n'utilise pas formsemestre_bulletinetud_dict()
#   pour simplifier le code, mais attention a la maintenance !)
#
def formsemestre_bulletinetud_published_dict(
    formsemestre_id,
    etudid,
    force_publishing=False,
    xml_nodate=False,
    xml_with_decisions=False,  # inclue les decisions même si non publiées
    version="long",
):
    """Dictionnaire representant les informations _publiees_ du bulletin de notes
    Utilisé pour JSON, devrait l'être aussi pour XML. (todo)
    """
    from app.scodoc import sco_bulletins

    formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
    etud = Identite.query.get(etudid)
    sem = sco_formsemestre.get_formsemestre(formsemestre_id)

    nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
    d = {"type": "classic", "version": "0"}

    if (not sem["bul_hide_xml"]) or force_publishing:
        published = True
    else:
        published = False
    if xml_nodate:
        docdate = ""
    else:
        docdate = datetime.datetime.now().isoformat()

    el = {
        "etudid": etudid,
        "formsemestre_id": formsemestre_id,
        "date": docdate,
        "publie": published,
        "etapes": sem["etapes"],
    }
    # backward compat:
    if sem["etapes"]:
        el["etape_apo"] = sem["etapes"][0] or ""
        n = 2
        for et in sem["etapes"][1:]:
            el["etape_apo" + str(n)] = et or ""
            n += 1
    d.update(**el)

    # Infos sur l'etudiant
    etudinfo = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]

    d["etudiant"] = dict(
        etudid=etudid,
        code_nip=etudinfo["code_nip"],
        code_ine=etudinfo["code_ine"],
        nom=scu.quote_xml_attr(etudinfo["nom"]),
        prenom=scu.quote_xml_attr(etudinfo["prenom"]),
        civilite=scu.quote_xml_attr(etudinfo["civilite_str"]),
        photo_url=scu.quote_xml_attr(sco_photos.etud_photo_url(etudinfo, fast=True)),
        email=scu.quote_xml_attr(etudinfo["email"]),
        emailperso=scu.quote_xml_attr(etudinfo["emailperso"]),
    )
    d["etudiant"]["sexe"] = d["etudiant"]["civilite"]  # backward compat for our clients
    # Disponible pour publication ?
    if not published:
        return d  # stop !

    etat_inscription = etud.inscription_etat(formsemestre.id)
    if etat_inscription != scu.INSCRIT:
        d.update(dict_decision_jury(etudid, formsemestre_id, with_decisions=True))
        return d

    # Groupes:
    partitions = sco_groups.get_partitions_list(formsemestre_id, with_default=False)
    partitions_etud_groups = {}  # { partition_id : { etudid : group } }
    for partition in partitions:
        pid = partition["partition_id"]
        partitions_etud_groups[pid] = sco_groups.get_etud_groups_in_partition(pid)

    ues = nt.get_ues_stat_dict()
    modimpls = nt.get_modimpls_dict()
    nbetuds = len(nt.etud_moy_gen_ranks)
    mg = scu.fmt_note(nt.get_etud_moy_gen(etudid))
    if (
        nt.get_moduleimpls_attente()
        or sco_preferences.get_preference("bul_show_rangs", formsemestre_id) == 0
    ):
        # n'affiche pas le rang sur le bulletin s'il y a des
        # notes en attente dans ce semestre
        rang = ""
        rang_gr = {}
        ninscrits_gr = {}
    else:
        rang = str(nt.get_etud_rang(etudid))
        rang_gr, ninscrits_gr, gr_name = sco_bulletins.get_etud_rangs_groups(
            etudid, formsemestre_id, partitions, partitions_etud_groups, nt
        )

    d["note"] = dict(
        value=mg,
        min=scu.fmt_note(nt.moy_min),
        max=scu.fmt_note(nt.moy_max),
        moy=scu.fmt_note(nt.moy_moy),
    )
    d["rang"] = dict(value=rang, ninscrits=nbetuds)
    d["rang_group"] = []
    if rang_gr:
        for partition in partitions:
            d["rang_group"].append(
                dict(
                    group_type=partition["partition_name"],
                    group_name=gr_name[partition["partition_id"]],
                    value=rang_gr[partition["partition_id"]],
                    ninscrits=ninscrits_gr[partition["partition_id"]],
                )
            )

    d["note_max"] = dict(value=20)  # notes toujours sur 20
    d["bonus_sport_culture"] = dict(
        value=nt.bonus[etudid] if nt.bonus is not None else 0.0
    )

    # Liste les UE / modules /evals
    d["ue"] = []
    d["ue_capitalisee"] = []
    for ue in ues:
        ue_status = nt.get_etud_ue_status(etudid, ue["ue_id"])
        if ue["ects"] is None:
            ects_txt = ""
        else:
            ects_txt = f"{ue['ects']:2.3g}"
        rang, effectif = nt.get_etud_ue_rang(ue["ue_id"], etudid)
        u = dict(
            id=ue["ue_id"],
            numero=scu.quote_xml_attr(ue["numero"]),
            acronyme=scu.quote_xml_attr(ue["acronyme"]),
            titre=scu.quote_xml_attr(ue["titre"]),
            note=dict(
                value=scu.fmt_note(ue_status["cur_moy_ue"] if ue_status else ""),
                min=scu.fmt_note(ue["min"]),
                max=scu.fmt_note(ue["max"]),
                moy=scu.fmt_note(
                    ue["moy"]
                ),  # CM : ajout pour faire apparaitre la moyenne des UE
            ),
            rang=rang,
            effectif=effectif,
            ects=ects_txt,
            code_apogee=scu.quote_xml_attr(ue["code_apogee"]),
        )
        d["ue"].append(u)
        u["module"] = []
        # Liste les modules de l'UE
        ue_modimpls = [mod for mod in modimpls if mod["module"]["ue_id"] == ue["ue_id"]]
        for modimpl in ue_modimpls:
            mod_moy = scu.fmt_note(
                nt.get_etud_mod_moy(modimpl["moduleimpl_id"], etudid)
            )
            if mod_moy == "NI":  # ne mentionne pas les modules ou n'est pas inscrit
                continue
            mod = modimpl["module"]
            # if mod['ects'] is None:
            #    ects = ''
            # else:
            #    ects = str(mod['ects'])
            modstat = nt.get_mod_stats(modimpl["moduleimpl_id"])

            m = dict(
                id=modimpl["moduleimpl_id"],
                code=mod["code"],
                coefficient=mod["coefficient"],
                numero=mod["numero"],
                titre=scu.quote_xml_attr(mod["titre"]),
                abbrev=scu.quote_xml_attr(mod["abbrev"]),
                # ects=ects, ects des modules maintenant inutilisés
                note=dict(value=mod_moy),
                code_apogee=scu.quote_xml_attr(mod["code_apogee"]),
            )
            m["note"].update(modstat)
            for k in ("min", "max", "moy"):  # formatte toutes les notes
                m["note"][k] = scu.fmt_note(m["note"][k])

            u["module"].append(m)
            if (
                sco_preferences.get_preference("bul_show_mod_rangs", formsemestre_id)
                and nt.mod_rangs is not None
            ):
                m["rang"] = dict(
                    value=nt.mod_rangs[modimpl["moduleimpl_id"]][0][etudid]
                )
                m["effectif"] = dict(value=nt.mod_rangs[modimpl["moduleimpl_id"]][1])

            # --- notes de chaque eval:
            evals = nt.get_evals_in_mod(modimpl["moduleimpl_id"])
            m["evaluation"] = []
            if version != "short":
                for e in evals:
                    if e["visibulletin"] or version == "long":
                        val = e["notes"].get(etudid, {"value": "NP"})[
                            "value"
                        ]  # NA si etud demissionnaire
                        val = scu.fmt_note(val, note_max=e["note_max"])
                        m["evaluation"].append(
                            dict(
                                jour=ndb.DateDMYtoISO(e["jour"], null_is_empty=True),
                                heure_debut=ndb.TimetoISO8601(
                                    e["heure_debut"], null_is_empty=True
                                ),
                                heure_fin=ndb.TimetoISO8601(
                                    e["heure_fin"], null_is_empty=True
                                ),
                                coefficient=e["coefficient"],
                                evaluation_type=e["evaluation_type"],
                                evaluation_id=e[
                                    "evaluation_id"
                                ],  # CM : ajout pour permettre de faire le lien sur les bulletins en ligne avec l'évaluation
                                description=scu.quote_xml_attr(e["description"]),
                                note=val,
                            )
                        )
                # Evaluations incomplètes ou futures:
                complete_eval_ids = set([e["evaluation_id"] for e in evals])
                if sco_preferences.get_preference(
                    "bul_show_all_evals", formsemestre_id
                ):
                    all_evals = sco_evaluation_db.do_evaluation_list(
                        args={"moduleimpl_id": modimpl["moduleimpl_id"]}
                    )
                    all_evals.reverse()  # plus ancienne d'abord
                    for e in all_evals:
                        if e["evaluation_id"] not in complete_eval_ids:
                            m["evaluation"].append(
                                dict(
                                    jour=ndb.DateDMYtoISO(
                                        e["jour"], null_is_empty=True
                                    ),
                                    heure_debut=ndb.TimetoISO8601(
                                        e["heure_debut"], null_is_empty=True
                                    ),
                                    heure_fin=ndb.TimetoISO8601(
                                        e["heure_fin"], null_is_empty=True
                                    ),
                                    coefficient=e["coefficient"],
                                    description=scu.quote_xml_attr(e["description"]),
                                    incomplete="1",
                                )
                            )

        # UE capitalisee (listee seulement si meilleure que l'UE courante)
        if ue_status["is_capitalized"]:
            try:
                ects_txt = str(int(ue_status["ue"].get("ects", "")))
            except:
                ects_txt = ""
            d["ue_capitalisee"].append(
                dict(
                    id=ue["ue_id"],
                    numero=scu.quote_xml_attr(ue["numero"]),
                    acronyme=scu.quote_xml_attr(ue["acronyme"]),
                    titre=scu.quote_xml_attr(ue["titre"]),
                    note=scu.fmt_note(ue_status["moy"]),
                    coefficient_ue=scu.fmt_note(ue_status["coef_ue"]),
                    date_capitalisation=ndb.DateDMYtoISO(ue_status["event_date"]),
                    ects=ects_txt,
                )
            )

    # --- Absences
    if sco_preferences.get_preference("bul_show_abs", formsemestre_id):
        nbabs, nbabsjust = sco_abs.get_abs_count(etudid, sem)
        d["absences"] = dict(nbabs=nbabs, nbabsjust=nbabsjust)

    # --- Decision Jury
    d.update(
        dict_decision_jury(etudid, formsemestre_id, with_decisions=xml_with_decisions)
    )
    # --- Appreciations
    cnx = ndb.GetDBConnexion()
    apprecs = sco_etud.appreciations_list(
        cnx, args={"etudid": etudid, "formsemestre_id": formsemestre_id}
    )
    d["appreciation"] = []
    for app in apprecs:
        d["appreciation"].append(
            dict(
                comment=scu.quote_xml_attr(app["comment"]),
                date=ndb.DateDMYtoISO(app["date"]),
            )
        )

    #
    return d


def dict_decision_jury(etudid, formsemestre_id, with_decisions=False):
    "dict avec decision pour bulletins json"
    from app.scodoc import sco_bulletins

    d = {}
    if (
        sco_preferences.get_preference("bul_show_decision", formsemestre_id)
        or with_decisions
    ):
        infos, dpv = sco_bulletins.etud_descr_situation_semestre(
            etudid,
            formsemestre_id,
            show_uevalid=sco_preferences.get_preference(
                "bul_show_uevalid", formsemestre_id
            ),
        )
        d["situation"] = infos["situation"]
        if dpv:
            decision = dpv["decisions"][0]
            etat = decision["etat"]
            if decision["decision_sem"]:
                code = decision["decision_sem"]["code"]
                date = ndb.DateDMYtoISO(
                    dpv["decisions"][0]["decision_sem"]["event_date"]
                )
            else:
                code = ""
                date = ""

            d["decision"] = dict(
                code=code,
                etat=etat,
                date=date,
            )
            if (
                decision["decision_sem"]
                and "compense_formsemestre_id" in decision["decision_sem"]
            ):
                d["decision"]["compense_formsemestre_id"] = decision["decision_sem"][
                    "compense_formsemestre_id"
                ]

            d["decision_ue"] = []
            if decision[
                "decisions_ue"
            ]:  # and sco_preferences.get_preference( 'bul_show_uevalid', formsemestre_id): always publish (car utile pour export Apogee)
                for ue_id in decision["decisions_ue"].keys():
                    ue = sco_edit_ue.ue_list({"ue_id": ue_id})[0]
                    d["decision_ue"].append(
                        dict(
                            ue_id=ue["ue_id"],
                            numero=ue["numero"],
                            acronyme=ue["acronyme"],
                            titre=ue["titre"],
                            code=decision["decisions_ue"][ue_id]["code"],
                            ects=ue["ects"] or "",
                        )
                    )
            d["autorisation_inscription"] = []
            for aut in decision["autorisations"]:
                d["autorisation_inscription"].append(
                    dict(semestre_id=aut["semestre_id"])
                )
        else:
            d["decision"] = dict(code="", etat="DEM")
    return d