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

##############################################################################
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2023 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
#
##############################################################################

"""Vérification des absences à une évaluation
"""
from flask import url_for, g

import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
from app.scodoc import html_sco_header
from app.scodoc import sco_abs
from app.scodoc import sco_etud
from app.scodoc import sco_evaluations
from app.scodoc import sco_evaluation_db
from app.scodoc import sco_formsemestre
from app.scodoc import sco_groups
from app.scodoc import sco_moduleimpl

# matin et/ou après-midi ?
def _eval_demijournee(E):
    "1 si matin, 0 si apres midi, 2 si toute la journee"
    am, pm = False, False
    if E["heure_debut"] < "13:00":
        am = True
    if E["heure_fin"] > "13:00":
        pm = True
    if am and pm:
        demijournee = 2
    elif am:
        demijournee = 1
    else:
        demijournee = 0
        pm = True
    return am, pm, demijournee


def evaluation_check_absences(evaluation_id):
    """Vérifie les absences au moment de cette évaluation.
    Cas incohérents que l'on peut rencontrer pour chaque étudiant:
      note et absent
      ABS et pas noté absent
      ABS et absent justifié
      EXC et pas noté absent
      EXC et pas justifie
    Ramene 3 listes d'etudid
    """
    E = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})[0]
    if not E["jour"]:
        return [], [], [], [], []  # evaluation sans date

    am, pm, demijournee = _eval_demijournee(E)

    # Liste les absences à ce moment:
    A = sco_abs.list_abs_jour(ndb.DateDMYtoISO(E["jour"]), am=am, pm=pm)
    As = set([x["etudid"] for x in A])  # ensemble des etudiants absents
    NJ = sco_abs.list_abs_non_just_jour(ndb.DateDMYtoISO(E["jour"]), am=am, pm=pm)
    NJs = set([x["etudid"] for x in NJ])  # ensemble des etudiants absents non justifies
    Just = sco_abs.list_abs_jour(
        ndb.DateDMYtoISO(E["jour"]), am=am, pm=pm, is_abs=None, is_just=True
    )
    Justs = set([x["etudid"] for x in Just])  # ensemble des etudiants avec justif

    # Les notes:
    notes_db = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id)
    ValButAbs = []  # une note mais noté absent
    AbsNonSignalee = []  # note ABS mais pas noté absent
    ExcNonSignalee = []  # note EXC mais pas noté absent
    ExcNonJust = []  #  note EXC mais absent non justifie
    AbsButExc = []  # note ABS mais justifié
    for etudid, _ in sco_groups.do_evaluation_listeetuds_groups(
        evaluation_id, getallstudents=True
    ):
        if etudid in notes_db:
            val = notes_db[etudid]["value"]
            if (
                val != None and val != scu.NOTES_NEUTRALISE and val != scu.NOTES_ATTENTE
            ) and etudid in As:
                # note valide et absent
                ValButAbs.append(etudid)
            if val is None and not etudid in As:
                # absent mais pas signale comme tel
                AbsNonSignalee.append(etudid)
            if val == scu.NOTES_NEUTRALISE and not etudid in As:
                # Neutralisé mais pas signale absent
                ExcNonSignalee.append(etudid)
            if val == scu.NOTES_NEUTRALISE and etudid in NJs:
                # EXC mais pas justifié
                ExcNonJust.append(etudid)
            if val is None and etudid in Justs:
                # ABS mais justificatif
                AbsButExc.append(etudid)

    return ValButAbs, AbsNonSignalee, ExcNonSignalee, ExcNonJust, AbsButExc


def evaluation_check_absences_html(evaluation_id, with_header=True, show_ok=True):
    """Affiche état vérification absences d'une évaluation"""

    E = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})[0]
    am, pm, demijournee = _eval_demijournee(E)

    (
        ValButAbs,
        AbsNonSignalee,
        ExcNonSignalee,
        ExcNonJust,
        AbsButExc,
    ) = evaluation_check_absences(evaluation_id)

    if with_header:
        H = [
            html_sco_header.html_sem_header("Vérification absences à l'évaluation"),
            sco_evaluations.evaluation_describe(evaluation_id=evaluation_id),
            """<p class="help">Vérification de la cohérence entre les notes saisies et les absences signalées.</p>""",
        ]
    else:
        # pas de header, mais un titre
        H = [
            """<h2 class="eval_check_absences">%s du %s """
            % (E["description"], E["jour"])
        ]
        if (
            not ValButAbs
            and not AbsNonSignalee
            and not ExcNonSignalee
            and not ExcNonJust
        ):
            H.append(': <span class="eval_check_absences_ok">ok</span>')
        H.append("</h2>")

    def etudlist(etudids, linkabs=False):
        H.append("<ul>")
        if not etudids and show_ok:
            H.append("<li>aucun</li>")
        for etudid in etudids:
            etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
            H.append(
                '<li><a class="discretelink" href="%s">'
                % url_for(
                    "scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etud["etudid"]
                )
                + "%(nomprenom)s</a>" % etud
            )
            if linkabs:
                H.append(
                    f"""<a class="stdlink" href="{url_for(
                    'absences.doSignaleAbsence',
                    scodoc_dept=g.scodoc_dept,
                    etudid=etud["etudid"],
                    datedebut=E["jour"],
                    datefin=E["jour"],
                    demijournee=demijournee,
                    moduleimpl_id=E["moduleimpl_id"],
                    )
                    }">signaler cette absence</a>"""
                )
            H.append("</li>")
        H.append("</ul>")

    if ValButAbs or show_ok:
        H.append(
            "<h3>Etudiants ayant une note alors qu'ils sont signalés absents:</h3>"
        )
        etudlist(ValButAbs)

    if AbsNonSignalee or show_ok:
        H.append(
            """<h3>Etudiants avec note "ABS" alors qu'ils ne sont <em>pas</em> signalés absents:</h3>"""
        )
        etudlist(AbsNonSignalee, linkabs=True)

    if ExcNonSignalee or show_ok:
        H.append(
            """<h3>Etudiants avec note "EXC" alors qu'ils ne sont <em>pas</em> signalés absents:</h3>"""
        )
        etudlist(ExcNonSignalee)

    if ExcNonJust or show_ok:
        H.append(
            """<h3>Etudiants avec note "EXC" alors qu'ils sont absents <em>non justifiés</em>:</h3>"""
        )
        etudlist(ExcNonJust)

    if AbsButExc or show_ok:
        H.append(
            """<h3>Etudiants avec note "ABS" alors qu'ils ont une <em>justification</em>:</h3>"""
        )
        etudlist(AbsButExc)

    if with_header:
        H.append(html_sco_header.sco_footer())
    return "\n".join(H)


def formsemestre_check_absences_html(formsemestre_id):
    """Affiche etat verification absences pour toutes les evaluations du semestre !"""
    sem = sco_formsemestre.get_formsemestre(formsemestre_id)
    H = [
        html_sco_header.html_sem_header(
            "Vérification absences aux évaluations de ce semestre",
        ),
        """<p class="help">Vérification de la cohérence entre les notes saisies et les absences signalées.
          Sont listés tous les modules avec des évaluations.<br>Aucune action n'est effectuée:
          il vous appartient de corriger les erreurs détectées si vous le jugez nécessaire.
          </p>""",
    ]
    # Modules, dans l'ordre
    Mlist = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id)
    for M in Mlist:
        evals = sco_evaluation_db.do_evaluation_list(
            {"moduleimpl_id": M["moduleimpl_id"]}
        )
        if evals:
            H.append(
                '<div class="module_check_absences"><h2><a href="moduleimpl_status?moduleimpl_id=%s">%s: %s</a></h2>'
                % (
                    M["moduleimpl_id"],
                    M["module"]["code"] or "",
                    M["module"]["abbrev"] or "",
                )
            )
        for E in evals:
            H.append(
                evaluation_check_absences_html(
                    E["evaluation_id"],
                    with_header=False,
                    show_ok=False,
                )
            )
        if evals:
            H.append("</div>")
    H.append(html_sco_header.sco_footer())
    return "\n".join(H)