# -*- 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@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 import log
from app import models
from app.models.formsemestre import FormSemestre
from app.models.moduleimpls import ModuleImpl
import app.scodoc.sco_utils as scu
from app.scodoc.sco_utils import ModuleType
from app.scodoc.sco_exceptions import AccessDenied, 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


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 = models.Evaluation.query.get(evaluation_id)
        moduleimpl_id = evaluation.moduleimpl_id
    #
    modimpl_o = sco_moduleimpl.moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[
        0
    ]
    mod = modimpl_o["module"]
    formsemestre_id = modimpl_o["formsemestre_id"]
    sem = FormSemestre.query.get(formsemestre_id)
    sem_ues = sem.query_ues(with_sport=False).all()
    is_malus = mod["module_type"] == ModuleType.MALUS
    is_apc = mod["module_type"] in (ModuleType.RESSOURCE, ModuleType.SAE)

    min_note_max = scu.NOTES_PRECISION  # le plus petit bareme possible
    #
    if not sco_permissions_check.can_edit_evaluation(moduleimpl_id=moduleimpl_id):
        return (
            html_sco_header.sco_header()
            + "<h2>Opération non autorisée</h2><p>"
            + "Modification évaluation impossible pour %s"
            % current_user.get_nomplogin()
            + "</p>"
            + '<p><a href="moduleimpl_status?moduleimpl_id=%s">Revenir</a></p>'
            % (moduleimpl_id,)
            + 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"
    #
    mod_descr = '<a href="moduleimpl_status?moduleimpl_id=%s">%s %s</a> %s' % (
        moduleimpl_id,
        mod["code"] or "module sans code",
        mod["titre"],
        link,
    )
    H = [
        f"""<h3>{action} en 
        {scu.MODULE_TYPE_NAMES[mod["module_type"]]} {mod_descr}</h3>
        """
    ]

    heures = ["%02dh%02d" % (h, m) 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 = {}
    if is_apc:  # BUT: poids vers les UE
        ue_coef_dict = ModuleImpl.query.get(moduleimpl_id).module.get_ue_coef_dict()
        for ue in sem_ues:
            if edit:
                existing_poids = models.EvaluationUEPoids.query.filter_by(
                    ue=ue, evaluation=evaluation
                ).first()
            else:
                existing_poids = None
            if existing_poids:
                poids = existing_poids.poids
            else:
                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', { 'title' : 'Date (j/m/a)', 'size' : 12, 'explanation' : 'date de l\'examen, devoir ou contrôle' }),
        (
            "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",
                    "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": "barème (note max actuelle: %s)" % 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:
        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,
                    },
                ),
            )
    tf = TrivialFormulator(
        request.base_url,
        vals,
        form,
        cancelbutton="Annuler",
        submitlabel=submitlabel,
        initvalues=initvalues,
        readonly=False,
    )

    dest_url = "moduleimpl_status?moduleimpl_id=%s" % modimpl_o["moduleimpl_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.html", 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
            evaluation_id = sco_evaluation_db.do_evaluation_create(**tf[2])
        if is_apc:
            # Set poids
            evaluation = models.Evaluation.query.get(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)