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

##############################################################################
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2024 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
from flask_sqlalchemy.query import Query

from app import db
from app.models import Evaluation, FormSemestre, Identite, Assiduite
import app.scodoc.sco_utils as scu
from app.scodoc import html_sco_header
from app.scodoc import sco_evaluations
from app.scodoc import sco_evaluation_db
from app.scodoc import sco_groups


def evaluation_check_absences(evaluation: Evaluation):
    """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 5 listes d'etudid
    """
    if not evaluation.date_debut or not evaluation.date_fin:
        return [], [], [], [], []  # evaluation sans date

    etudids = [
        etudid
        for etudid, _ in sco_groups.do_evaluation_listeetuds_groups(
            evaluation.id, getallstudents=True
        )
    ]

    deb, fin = scu.localize_datetime(evaluation.date_debut), scu.localize_datetime(
        evaluation.date_fin
    )

    assiduites: Query = Assiduite.query.filter(
        Assiduite.etudid.in_(etudids),
        Assiduite.etat == scu.EtatAssiduite.ABSENT,
        fin >= Assiduite.date_debut,
        deb <= Assiduite.date_fin,
    )

    abs_etudids = set(assi.etudid for assi in assiduites)
    abs_nj_etudids = set(assi.etudid for assi in assiduites if assi.est_just is False)
    just_etudids = set(assi.etudid for assi in assiduites if assi.est_just is True)

    # Les notes:
    notes_db = sco_evaluation_db.do_evaluation_get_all_notes(evaluation.id)
    note_but_abs = []  # une note mais noté absent
    abs_non_signalee = []  # note ABS mais pas noté absent
    exc_non_signalee = []  # note EXC mais pas noté absent
    exc_non_just = []  #  note EXC mais absent non justifie
    abs_but_exc = []  # note ABS mais justifié
    for etudid in etudids:
        if etudid in notes_db:
            val = notes_db[etudid]["value"]
            if (
                val is not None
                and val != scu.NOTES_NEUTRALISE
                and val != scu.NOTES_ATTENTE
            ) and etudid in abs_etudids:
                # note valide et absent
                note_but_abs.append(etudid)
            if val is None and not etudid in abs_etudids:
                # absent mais pas signale comme tel
                abs_non_signalee.append(etudid)
            if val == scu.NOTES_NEUTRALISE and not etudid in abs_etudids:
                # Neutralisé mais pas signale absent
                exc_non_signalee.append(etudid)
            if val == scu.NOTES_NEUTRALISE and etudid in abs_nj_etudids:
                # EXC mais pas justifié
                exc_non_just.append(etudid)
            if val is None and etudid in just_etudids:
                # ABS mais justificatif
                abs_but_exc.append(etudid)

    return note_but_abs, abs_non_signalee, exc_non_signalee, exc_non_just, abs_but_exc


def evaluation_check_absences_html(
    evaluation: Evaluation, with_header=True, show_ok=True
):
    """Affiche état vérification absences d'une évaluation"""
    (
        note_but_abs,  # une note alors qu'il était signalé abs
        abs_non_signalee,  # note ABS alors que pas signalé abs
        exc_non_signalee,  # note EXC alors que pas signalé abs
        exc_non_just,  # note EXC alors que pas de justif
        abs_but_exc,  # note ABS alors qu'il y a un justif
    ) = evaluation_check_absences(evaluation)

    if with_header:
        H = [
            html_sco_header.html_sem_header(
                "Vérification absences à l'évaluation",
                formsemestre_id=evaluation.moduleimpl.formsemestre_id,
            ),
            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 = [
            f"""<h2 class="eval_check_absences">{
                evaluation.description or "évaluation"
                } du {
                evaluation.date_debut.strftime("%d/%m/%Y") if evaluation.date_debut else ""
                } """
        ]
        if (
            not note_but_abs
            and not abs_non_signalee
            and not exc_non_signalee
            and not exc_non_just
        ):
            H.append(': <span class="eval_check_absences_ok">ok</span>')
        H.append("</h2>")

    def etudlist(etudids: list[int], linkabs=False):
        H.append("<ul>")
        if not etudids and show_ok:
            H.append("<li>aucun</li>")
        for etudid in etudids:
            etud: Identite = db.session.get(Identite, etudid)
            H.append(
                f"""<li><a class="discretelink" href="{
                    url_for(
                        "scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid
                    )
                }">{etud.nomprenom}</a>"""
            )
            if linkabs:
                url = url_for(
                    "assiduites.signale_evaluation_abs",
                    etudid=etudid,
                    evaluation_id=evaluation.id,
                    scodoc_dept=g.scodoc_dept,
                )
                H.append(
                    f"""<a style="margin-left: 16px;" class="stdlink" href="{
                        url}">signaler cette absence</a>"""
                )
            H.append("</li>")
        H.append("</ul>")

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

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

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

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

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

    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 !"""
    formsemestre: FormSemestre = FormSemestre.query.filter_by(
        dept_id=g.scodoc_dept_id, id=formsemestre_id
    ).first_or_404()
    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
    for modimpl in formsemestre.modimpls_sorted:
        if modimpl.evaluations.count() > 0:
            H.append(
                f"""<div class="module_check_absences">
                    <h2><a href="{
                        url_for("notes.moduleimpl_status",
                                scodoc_dept=g.scodoc_dept, moduleimpl_id=modimpl.id)
                    }">{modimpl.module.code or ""}: {modimpl.module.abbrev or ""}</a>
                    </h2>"""
            )
            for evaluation in modimpl.evaluations:
                H.append(
                    evaluation_check_absences_html(
                        evaluation,
                        with_header=False,
                        show_ok=False,
                    )
                )
            H.append("</div>")

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