# -*- 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
#
##############################################################################

"""Rapport sur réussite en BUT pour enquête 2022
  - statistiques decisions
"""
from collections import defaultdict


from flask import request

from app import db
from app.but import jury_but
from app.models import FormSemestre
from app.models.formsemestre import FormSemestreInscription

import app.scodoc.sco_utils as scu
from app.scodoc import html_sco_header
from app.scodoc import codes_cursus
from app.scodoc.sco_exceptions import ScoValueError

from app.scodoc import sco_preferences
import sco_version
from app.scodoc.gen_tables import GenTable


# Titres, ordonnés
INDICATEUR_NAMES = {
    "nb_inscr_S1": "Inscrits initiaux S1",
    "nb_dem_S1": "Démissions S1",
    "nb_def_S1": "Défaillants S1",
    "nb_actifs_S1": "Inscrits finals S1",
    "nb_inscr_S2": "Inscrits initiaux S2",
    "nb_dem_S2": "Démissions S2",
    "nb_def_S2": "Défaillants S2",
    "nb_actifs_S2": "Inscrits finals S2",
    "nb_no_decision": "Sans décision de jury BUT",
    "nb_nar": "NAR",
    "nb_passe_manque_rcue": "Passant avec RCUE non validé",
    "nb_red_avec_rcue": "Redoublant avec au moins un RCUE validé",
    "nb_red_sans_rcue": "Redoublant sans avoir validé aucun RCUE",
    "nb_valide_tt_rcue": "Validant tous les RCUE de l'année",
}


def formsemestre_but_indicateurs(formsemestre_id: int, fmt="html"):
    """Page avec tableau indicateurs enquête ADIUT BUT 2022"""
    formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)

    indicateurs_by_bac = but_indicateurs_by_bac(formsemestre)
    # finalement on fait une table avec en ligne
    # les indicateurs, et en colonne les bacs
    bacs = sorted(indicateurs_by_bac.keys())

    rows = []
    for indicateur, titre_indicateur in INDICATEUR_NAMES.items():
        row = {bac: indicateurs_by_bac[bac][indicateur] for bac in bacs}
        row["titre_indicateur"] = titre_indicateur
        rows.append(row)

    # ensure Total is the rightmost column
    del bacs[bacs.index("Total")]
    bacs.append("Total")

    tab = GenTable(
        titles={bac: bac for bac in bacs},
        columns_ids=["titre_indicateur"] + bacs,
        rows=rows,
        html_sortable=False,
        preferences=sco_preferences.SemPreferences(formsemestre_id),
        filename=scu.make_filename(f"Indicateurs_BUT_{formsemestre.titre_annee()}"),
        origin=f"Généré par {sco_version.SCONAME} le {scu.timedate_human_repr()}",
        html_caption="Indicateurs BUT annuels.",
        base_url=f"{request.base_url}?formsemestre_id={formsemestre_id}",
    )
    title = "Indicateurs suivi annuel BUT"
    t = tab.make_page(
        title=f"""<h2 class="formsemestre">{title}</h2>""",
        fmt=fmt,
        with_html_headers=False,
    )
    if fmt != "html":
        return t
    H = [
        html_sco_header.sco_header(page_title=title),
        t,
        """<p class="help">
          </p>""",
        html_sco_header.sco_footer(),
    ]
    return "\n".join(H)


def but_indicateurs_by_bac(formsemestre: FormSemestre) -> dict[str:dict]:
    """
    L'enquête ADIUT porte sur le nombre de
    - inscrits
    - ayant validé tous les RCUE
    - passant en BUT2 sans avoir validé tous les RCUE
    - redoublants avec au moins une RCUE
    - redoublants sans aucune RCUE
    - NAR
    - défaillants
    - démissionnaires,
    le tout découpé en FI/FA et suivant le type de bac : général/techno/pro/autre.

    Le semestre est FI ou FA, donc on ne traite pas ce point.
    On suppose qu'on est sur un semestre PAIR de BUT, dont les décisions de jury
    ont déjà été saisies.
    """
    if not formsemestre.formation.is_apc():
        raise ScoValueError(
            "Ce rapport doit être généré à partir d'une formation par compétences (BUT)."
        )
    if formsemestre.semestre_id % 2:
        raise ScoValueError("Ce rapport doit être généré à partir d'un semestre PAIR.")
    # Les semestres suivant/precedent (pour compter les passages et redoublements)
    next_sem_idx = formsemestre.semestre_id + 1
    red_sem_idx = formsemestre.semestre_id - 1

    # Ventilation par bac
    inscriptions_by_bac = _formsemestre_inscriptions_by_bac(formsemestre)
    indicateurs_by_bac = {}
    decisions_annee_tous = {}
    for bac in inscriptions_by_bac:
        decisions_annee = {
            inscr.etud.id: jury_but.DecisionsProposeesAnnee(inscr.etud, formsemestre)
            for inscr in inscriptions_by_bac[bac]
        }
        indicateurs_by_bac[bac] = _indicateurs_enquete_but(
            inscriptions_by_bac[bac], decisions_annee, red_sem_idx, next_sem_idx
        )
        decisions_annee_tous.update(decisions_annee)
    # refait pour tous
    indicateurs_by_bac["Total"] = _indicateurs_enquete_but(
        formsemestre.inscriptions, decisions_annee_tous, red_sem_idx, next_sem_idx
    )
    # Comptages sur semestre(s) précédent(s)
    # en effet, les étudiants de ce semestre pair peuvent venir de
    # différents S1
    formsemestre_id_precedents = {
        deca.formsemestre_impair.id
        for deca in decisions_annee_tous.values()
        if deca and deca.formsemestre_impair
    }
    for formsemestre_id_precedent in formsemestre_id_precedents:
        formsemestre_impair = db.session.get(FormSemestre, formsemestre_id_precedent)
        suffix = (
            f"S{formsemestre_impair.semestre_id}"
            if len(formsemestre_id_precedents) == 1
            else formsemestre_impair.session_id()
        )
        for bac, inscriptions in _formsemestre_inscriptions_by_bac(
            formsemestre_impair
        ).items():
            if bac not in indicateurs_by_bac:
                indicateurs_by_bac[bac] = defaultdict(lambda: 0)
            indicateurs_by_bac[bac].update(
                _indicateurs_enquete_but_inscrits(inscriptions, suffix)
            )
        indicateurs_by_bac["Total"].update(
            _indicateurs_enquete_but_inscrits(formsemestre_impair.inscriptions, suffix)
        )
    return indicateurs_by_bac


def _formsemestre_inscriptions_by_bac(formsemestre: FormSemestre) -> defaultdict:
    "liste d'inscriptions, par type de bac"
    inscriptions_by_bac = defaultdict(list)  # bac : etuds
    for inscr in formsemestre.inscriptions:
        adm = inscr.etud.admission
        bac = adm.get_bac().abbrev() if adm else "?"
        inscriptions_by_bac[bac].append(inscr)
    return inscriptions_by_bac


def _indicateurs_enquete_but_inscrits(
    inscriptions: list[FormSemestreInscription], suffix: str
) -> dict:
    """Nombre d'incrits, DEM, DEF.
    Suffixe les clés avec _suffix"""
    if suffix:
        suffix = "_" + suffix
    return defaultdict(
        lambda: "?",
        {
            "nb_inscr" + suffix: len(inscriptions),
            "nb_actifs"
            + suffix: len([i for i in inscriptions if i.etat == scu.INSCRIT]),
            "nb_def" + suffix: len([i for i in inscriptions if i.etat == scu.DEF]),
            "nb_dem"
            + suffix: len([i for i in inscriptions if i.etat == scu.DEMISSION]),
        },
    )


def _indicateurs_enquete_but(
    inscriptions: list[FormSemestreInscription],
    decisions_annee: dict[jury_but.DecisionsProposeesAnnee],
    red_sem_idx: int,
    next_sem_idx: int,
) -> dict:
    """Calcule les indicateurs de l'enquête ADIUT 2022"""
    indicateurs = _indicateurs_enquete_but_inscrits(inscriptions, suffix="S2")
    indicateurs.update(
        {
            "nb_no_decision": len(
                [True for deca in decisions_annee.values() if deca.code_valide is None]
            ),
            "nb_nar": len(
                [
                    True
                    for deca in decisions_annee.values()
                    if deca.code_valide == codes_cursus.NAR
                ]
            ),
            # Redoublants sans aucune RCUE
            "nb_red_sans_rcue": len(
                [
                    True
                    for deca in decisions_annee.values()
                    if (deca.nb_rcue_valides == 0)
                    and (next_sem_idx not in deca.get_autorisations_passage())
                    and (red_sem_idx in deca.get_autorisations_passage())
                ]
            ),
            # Redoublants avec au moins une RCUE
            "nb_red_avec_rcue": len(
                [
                    True
                    for deca in decisions_annee.values()
                    if (deca.nb_rcue_valides > 0)
                    and (next_sem_idx not in deca.get_autorisations_passage())
                    and (red_sem_idx in deca.get_autorisations_passage())
                ]
            ),
            # Passant (en BUT2) sans avoir validé tous les RCUE
            "nb_passe_manque_rcue": len(
                [
                    True
                    for deca in decisions_annee.values()
                    if (deca.nb_rcue_valides < deca.nb_competences)
                    and (next_sem_idx in deca.get_autorisations_passage())
                ]
            ),
            # Ayant validé tous les RCUE
            "nb_valide_tt_rcue": len(
                [
                    True
                    for deca in decisions_annee.values()
                    if (deca.nb_rcue_valides >= deca.nb_competences)
                ]
            ),
        }
    )
    return indicateurs