# -*- 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@gmail.com
#
##############################################################################

"""Formulaire ajout/édition d'une évaluation
"""

import time

import flask
from flask import url_for, render_template
from flask import g
from flask_login import current_user
from flask import request

from app import db
from app.models import Evaluation, FormSemestre, ModuleImpl

import app.scodoc.sco_utils as scu
from app.scodoc.sco_utils import ModuleType
from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc.TrivialFormulator import TrivialFormulator
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_moduleimpl
from app.scodoc import sco_permissions_check
from app.scodoc import sco_preferences


def evaluation_create_form(
    moduleimpl_id=None,
    evaluation_id=None,
    edit=False,
    page_title="Évaluation",
):
    "Formulaire création/édition d'une évaluation (pas de ses notes)"
    if evaluation_id is not None:
        evaluation: Evaluation = db.session.get(Evaluation, evaluation_id)
        if evaluation is None:
            raise ScoValueError("Cette évaluation n'existe pas ou plus !")
        moduleimpl_id = evaluation.moduleimpl_id
    #
    modimpl: ModuleImpl = (
        ModuleImpl.query.filter_by(id=moduleimpl_id)
        .join(FormSemestre)
        .filter_by(dept_id=g.scodoc_dept_id)
        .first_or_404()
    )
    modimpl_o = sco_moduleimpl.moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[
        0
    ]
    mod = modimpl_o["module"]
    formsemestre_id = modimpl_o["formsemestre_id"]
    formsemestre = modimpl.formsemestre
    sem_ues = formsemestre.get_ues(with_sport=False)
    is_malus = mod["module_type"] == ModuleType.MALUS
    is_apc = mod["module_type"] in (ModuleType.RESSOURCE, ModuleType.SAE)
    preferences = sco_preferences.SemPreferences(formsemestre.id)
    can_edit_poids = not preferences["but_disable_edit_poids_evaluations"]
    min_note_max = scu.NOTES_PRECISION  # le plus petit bareme possible
    #
    if not sco_permissions_check.can_edit_evaluation(moduleimpl_id=moduleimpl_id):
        return f"""
        {html_sco_header.sco_header()}
        <h2>Opération non autorisée</h2>
        <p>Modification évaluation impossible pour {current_user.get_nomplogin()}</p>
        <p><a href="{url_for('notes.moduleimpl_status', 
            scodoc_dept=g.scodoc_dept, moduleimpl_id=moduleimpl_id)
            }" class="stdlink">Revenir</a>
        </p>
        {html_sco_header.sco_footer()}
        """
    if not edit:
        # création nouvel
        if moduleimpl_id is None:
            raise ValueError("missing moduleimpl_id parameter")
        initvalues = {
            "note_max": 20,
            "jour": time.strftime("%d/%m/%Y", time.localtime()),
            "publish_incomplete": is_malus,
        }
        submitlabel = "Créer cette évaluation"
        action = "Création d'une évaluation"
        link = ""
    else:
        # édition données existantes
        # setup form init values
        if evaluation_id is None:
            raise ValueError("missing evaluation_id parameter")
        initvalues = evaluation.to_dict()
        moduleimpl_id = initvalues["moduleimpl_id"]
        submitlabel = "Modifier les données"
        action = "Modification d'une évaluation"
        link = ""
        # Note maximale actuelle dans cette éval ?
        etat = sco_evaluations.do_evaluation_etat(evaluation_id)
        if etat["maxi_num"] is not None:
            min_note_max = max(scu.NOTES_PRECISION, etat["maxi_num"])
        else:
            min_note_max = scu.NOTES_PRECISION
    #
    if min_note_max > scu.NOTES_PRECISION:
        min_note_max_str = scu.fmt_note(min_note_max)
    else:
        min_note_max_str = "0"
    #
    H = [
        f"""<h3>{action} en
        {scu.MODULE_TYPE_NAMES[mod["module_type"]]} <a class="stdlink" href="{
            url_for("notes.moduleimpl_status", 
                scodoc_dept=g.scodoc_dept, moduleimpl_id=moduleimpl_id)
            }">{mod["code"] or "module sans code"} {mod["titre"]}</a> {link}</h3>
        """
    ]

    heures = [f"{h:02d}h{m:02d}" for h in range(8, 19) for m in (0, 30)]
    #
    initvalues["visibulletin"] = initvalues.get("visibulletin", True)
    if initvalues["visibulletin"]:
        initvalues["visibulletinlist"] = ["X"]
    else:
        initvalues["visibulletinlist"] = []
    initvalues["coefficient"] = initvalues.get("coefficient", 1.0)
    vals = scu.get_request_args()
    if vals.get("tf_submitted", False) and "visibulletinlist" not in vals:
        vals["visibulletinlist"] = []
    #
    ue_coef_dict = modimpl.module.get_ue_coef_dict()
    if is_apc:  # BUT: poids vers les UE
        if edit:
            if evaluation.set_default_poids():
                db.session.commit()
            ue_poids_dict = evaluation.get_ue_poids_dict()
            for ue in sem_ues:
                initvalues[f"poids_{ue.id}"] = ue_poids_dict[ue.id]
        else:
            for ue in sem_ues:
                coef_ue = ue_coef_dict.get(ue.id, 0.0) or 0.0
                if coef_ue > 0:
                    poids = 1.0  # par défaut au départ
                else:
                    poids = 0.0
                initvalues[f"poids_{ue.id}"] = poids
    #
    form = [
        ("evaluation_id", {"default": evaluation_id, "input_type": "hidden"}),
        ("formsemestre_id", {"default": formsemestre_id, "input_type": "hidden"}),
        ("moduleimpl_id", {"default": moduleimpl_id, "input_type": "hidden"}),
        (
            "jour",
            {
                "input_type": "datedmy",
                "title": "Date",
                "size": 12,
                "explanation": "date de l'examen, devoir ou contrôle",
            },
        ),
        (
            "heure_debut",
            {
                "title": "Heure de début",
                "explanation": "heure du début de l'épreuve",
                "input_type": "menu",
                "allowed_values": heures,
                "labels": heures,
            },
        ),
        (
            "heure_fin",
            {
                "title": "Heure de fin",
                "explanation": "heure de fin de l'épreuve",
                "input_type": "menu",
                "allowed_values": heures,
                "labels": heures,
            },
        ),
    ]
    if is_malus:  # pas de coefficient
        form.append(("coefficient", {"input_type": "hidden", "default": "1."}))
    elif not is_apc:  # modules standard hors BUT
        form.append(
            (
                "coefficient",
                {
                    "size": 6,
                    "type": "float",  # peut être négatif (!)
                    "explanation": "coef. dans le module (choisi librement par l'enseignant, non utilisé pour rattrapage et 2ème session)",
                    "allow_null": False,
                },
            )
        )
    form += [
        (
            "note_max",
            {
                "size": 4,
                "type": "float",
                "title": "Notes de 0 à",
                "explanation": f"barème (note max actuelle: {min_note_max_str})",
                "allow_null": False,
                "max_value": scu.NOTES_MAX,
                "min_value": min_note_max,
            },
        ),
        (
            "description",
            {
                "size": 36,
                "type": "text",
                "explanation": """type d'évaluation, apparait sur le bulletins longs. Exemples: "contrôle court", "examen de TP", "examen final".""",
            },
        ),
        (
            "visibulletinlist",
            {
                "input_type": "checkbox",
                "allowed_values": ["X"],
                "labels": [""],
                "title": "Visible sur bulletins",
                "explanation": "(pour les bulletins en version intermédiaire)",
            },
        ),
        (
            "publish_incomplete",
            {
                "input_type": "boolcheckbox",
                "title": "Prise en compte immédiate",
                "explanation": "notes utilisées même si incomplètes",
            },
        ),
        (
            "evaluation_type",
            {
                "input_type": "menu",
                "title": "Modalité",
                "allowed_values": (
                    scu.EVALUATION_NORMALE,
                    scu.EVALUATION_RATTRAPAGE,
                    scu.EVALUATION_SESSION2,
                ),
                "type": "int",
                "labels": (
                    "Normale",
                    "Rattrapage (remplace si meilleure note)",
                    "Deuxième session (remplace toujours)",
                ),
            },
        ),
    ]
    if is_apc:  # ressources et SAÉs
        form += [
            (
                "coefficient",
                {
                    "size": 6,
                    "type": "float",
                    "explanation": "importance de l'évaluation (multiplie les poids ci-dessous)",
                    "allow_null": False,
                },
            ),
        ]
        # Liste des UE utilisées dans des modules de ce semestre:
        if sem_ues and not can_edit_poids:
            form.append(
                (
                    "sep_poids",
                    {
                        "input_type": "separator",
                        "title": """
                    <div class="warning-light">les poids ne sont pas modifiables (voir réglage paramétrage)
                    </div>""",
                    },
                )
            )
        for ue in sem_ues:
            coef_ue = ue_coef_dict.get(ue.id, 0.0)
            form.append(
                (
                    f"poids_{ue.id}",
                    {
                        "title": f"Poids {ue.acronyme}",
                        "size": 2,
                        "type": "float",
                        "explanation": f"""
                            <span class="eval_coef_ue" title="coef. du module dans cette UE">({
                                "coef. mod.:" +str(coef_ue) if coef_ue 
                                else "ce module n'a pas de coef. dans cette UE"
                            })</span>
                            <span class="eval_coef_ue_titre">{ue.titre}</span>
                        """,
                        "allow_null": False,
                        # ok si poids nul ou coef vers l'UE nul:
                        "validator": lambda val, field: (not val)
                        or ue_coef_dict.get(int(field[len("poids_") :]), 0.0) != 0,
                        "enabled": (coef_ue != 0 or initvalues[f"poids_{ue.id}"] != 0.0)
                        and can_edit_poids,
                    },
                ),
            )
    tf = TrivialFormulator(
        request.base_url,
        vals,
        form,
        cancelbutton="Annuler",
        submitlabel=submitlabel,
        initvalues=initvalues,
        readonly=False,
    )

    dest_url = url_for(
        "notes.moduleimpl_status", scodoc_dept=g.scodoc_dept, moduleimpl_id=modimpl.id
    )
    if tf[0] == 0:
        head = html_sco_header.sco_header(page_title=page_title)
        return (
            head
            + "\n".join(H)
            + "\n"
            + tf[1]
            + render_template("scodoc/help/evaluations.j2", is_apc=is_apc)
            + html_sco_header.sco_footer()
        )
    elif tf[0] == -1:
        return flask.redirect(dest_url)
    else:
        # form submission
        if tf[2]["visibulletinlist"]:
            tf[2]["visibulletin"] = True
        else:
            tf[2]["visibulletin"] = False
        if edit:
            sco_evaluation_db.do_evaluation_edit(tf[2])
        else:
            # création d'une evaluation (via fonction ScoDoc7)
            evaluation_id = sco_evaluation_db.do_evaluation_create(**tf[2])
        if is_apc:
            # Set poids
            evaluation = db.session.get(Evaluation, evaluation_id)
            for ue in sem_ues:
                evaluation.set_ue_poids(ue, tf[2][f"poids_{ue.id}"])
            db.session.add(evaluation)
            db.session.commit()
        return flask.redirect(dest_url)