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

"""Test de base de ScoDoc


Utiliser comme:
    pytest tests/unit/test_sco_basic.py

Au besoin, créer un base de test neuve:
    ./tools/create_database.sh SCODOC_TEST

"""
import datetime

from app.models import Evaluation, FormSemestreInscription, Identite, ModuleImpl

from config import TestConfig
from tests.unit import sco_fake_gen
from tests.unit.setup import NOTES_T

import app
from app import db
from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat
from app.models import FormSemestre, Assiduite, Justificatif
from app.scodoc import sco_formsemestre
from app.scodoc import sco_bulletins
from app.scodoc import codes_cursus
from app.scodoc import sco_assiduites as scass
from app.scodoc import sco_evaluations
from app.scodoc import sco_formsemestre_validation
from app.scodoc import sco_cursus_dut
from app.scodoc import sco_saisie_notes
from app.scodoc.sco_utils import EtatAssiduite, EtatJustificatif, localize_datetime

DEPT = TestConfig.DEPT_TEST


def test_sco_basic(test_client):
    """Test quelques opérations élémentaires de ScoDoc
    Création 10 étudiants, formation, semestre, inscription etudiant,
    creation 1 evaluation, saisie 10 notes.
    """
    app.set_sco_dept(DEPT)
    run_sco_basic()


def run_sco_basic(verbose=False, dept=None) -> FormSemestre:
    """Scénario de base: création formation, semestre, étudiants, notes,
    décisions jury
    Renvoie le formsemestre créé.
    """
    G = sco_fake_gen.ScoFake(verbose=verbose, dept=dept)

    # --- Création d'étudiants
    etuds = [G.create_etud(code_nip=None) for _ in range(10)]

    # --- Création d'une formation
    formation_id = G.create_formation(acronyme="")
    ue_id = G.create_ue(formation_id=formation_id, acronyme="TST1", titre="ue test")
    matiere_id = G.create_matiere(ue_id=ue_id, titre="matière test")
    module_id = G.create_module(
        matiere_id=matiere_id,
        code="TSM1",
        coefficient=1.0,
        titre="module test",
    )

    # --- Mise place d'un semestre
    formsemestre_id = G.create_formsemestre(
        formation_id=formation_id,
        semestre_id=1,
        date_debut="01/01/2020",
        date_fin="30/06/2020",
    )
    sem = sco_formsemestre.get_formsemestre(formsemestre_id)
    moduleimpl_id = G.create_moduleimpl(
        module_id=module_id,
        formsemestre_id=formsemestre_id,
    )
    moduleimpl: ModuleImpl = db.session.get(ModuleImpl, moduleimpl_id)
    # --- Inscription des étudiants
    for etud in etuds:
        G.inscrit_etudiant(formsemestre_id, etud)

    # Vérification incription semestre:
    q = FormSemestreInscription.query.filter_by(
        etudid=etuds[0]["etudid"], formsemestre_id=formsemestre_id
    )
    assert q.count() == 1
    ins = q.first()
    assert ins.etape is None
    assert ins.etat == "I"
    assert ins.parcour is None

    # --- Création évaluation
    e1 = Evaluation.create(
        moduleimpl=moduleimpl,
        date_debut=datetime.datetime(2020, 1, 1),
        description="evaluation test",
        coefficient=1.0,
    )
    db.session.commit()

    # --- Saisie toutes les notes de l'évaluation
    for idx, etud in enumerate(etuds):
        etudids_changed, nb_suppress, existing_decisions = G.create_note(
            evaluation_id=e1.id,
            etudid=etud["etudid"],
            note=NOTES_T[idx % len(NOTES_T)],
        )
        assert not existing_decisions
        assert nb_suppress == 0
        assert len(etudids_changed) == 1

    # --- Vérifie que les notes sont prises en compte:
    b = sco_bulletins.formsemestre_bulletinetud_dict(formsemestre_id, etud["etudid"])
    # Toute les notes sont saisies, donc eval complète
    etat = sco_evaluations.do_evaluation_etat(e1.id)
    assert etat["evalcomplete"]
    assert etat["nb_inscrits"] == len(etuds)
    assert etat["nb_notes"] == len(etuds)
    # Un seul module, donc moy gen == note module
    assert b["ues"][0]["cur_moy_ue_txt"] == b["ues"][0]["modules"][0]["mod_moy_txt"]
    # Note au module égale à celle de l'éval
    assert (
        b["ues"][0]["modules"][0]["mod_moy_txt"]
        == b["ues"][0]["modules"][0]["evaluations"][0]["note_txt"]
    )

    # --- Une autre évaluation
    e2 = Evaluation.create(
        moduleimpl=moduleimpl,
        date_debut=datetime.datetime(2020, 1, 2),
        description="evaluation test 2",
        coefficient=1.0,
    )
    db.session.commit()
    # Saisie les notes des 5 premiers étudiants:
    for idx, etud in enumerate(etuds[:5]):
        etudids_changed, nb_suppress, existing_decisions = G.create_note(
            evaluation_id=e2.id,
            etudid=etud["etudid"],
            note=NOTES_T[idx % len(NOTES_T)],
        )
    # Cette éval n'est pas complète
    etat = sco_evaluations.do_evaluation_etat(e2.id)
    assert etat["evalcomplete"] is False
    # la première éval est toujours complète:
    etat = sco_evaluations.do_evaluation_etat(e1.id)
    assert etat["evalcomplete"]

    # Modifie l'évaluation 2 pour "prise en compte immédiate"
    e2.publish_incomplete = True
    db.session.add(e2)
    db.session.flush()
    etat = sco_evaluations.do_evaluation_etat(e2.id)
    assert etat["evalcomplete"] is False
    assert etat["nb_att"] == 0  # il n'y a pas de notes (explicitement) en attente
    assert etat["evalattente"]  # mais l'eval est en attente (prise en compte immédiate)

    # Saisie des notes qui manquent:
    for idx, etud in enumerate(etuds[5:]):
        etudids_changed, nb_suppress, existing_decisions = G.create_note(
            evaluation_id=e2.id,
            etudid=etud["etudid"],
            note=NOTES_T[idx % len(NOTES_T)],
        )
    etat = sco_evaluations.do_evaluation_etat(e2.id)
    assert etat["evalcomplete"]
    assert etat["nb_att"] == 0
    assert not etat["evalattente"]  # toutes les notes sont présentes

    # --- Suppression des notes
    sco_saisie_notes.evaluation_suppress_alln(e1.id, dialog_confirmed=True)
    etat = sco_evaluations.do_evaluation_etat(e1.id)
    assert etat["nb_notes"] == 0
    assert not etat["evalcomplete"]
    # --- Saisie des notes manquantes
    ans = sco_saisie_notes.do_evaluation_set_missing(
        e1.id, 12.34, dialog_confirmed=True
    )
    assert f'{etat["nb_inscrits"]} notes changées' in ans
    etat = sco_evaluations.do_evaluation_etat(e1.id)
    assert etat["evalcomplete"]

    # -----------------------
    # --- Saisie absences ---
    # -----------------------
    etudid = etuds[0]["etudid"]

    _signal_absences_justificatifs(etudid)
    _, nbabsjust, nbabs = scass.get_assiduites_count(etudid, sem)
    assert nbabs == 6, f"incorrect nbabs ({nbabs})"
    assert nbabsjust == 2, f"incorrect nbabsjust ({nbabsjust})"

    # --- Permission saisie notes et décisions de jury, avec ou sans démission ou défaillance
    # on n'a pas encore saisi de décisions
    assert not sco_cursus_dut.formsemestre_has_decisions(formsemestre_id)
    # Saisie d'un décision AJ, non assidu
    etudid = etuds[-1]["etudid"]
    sco_cursus_dut.formsemestre_validate_ues(
        formsemestre_id, etudid, codes_cursus.AJ, False
    )
    assert sco_cursus_dut.formsemestre_has_decisions(
        formsemestre_id
    ), "décisions manquantes"
    # Suppression de la décision
    sco_formsemestre_validation.formsemestre_validation_suppress_etud(
        formsemestre_id, etudid
    )
    assert not sco_cursus_dut.formsemestre_has_decisions(
        formsemestre_id
    ), "décisions non effacées"

    # --- Décision de jury et validations des ECTS d'UE
    for etud in etuds[:5]:  # les etudiants notés
        sco_formsemestre_validation.formsemestre_validation_etud_manu(
            formsemestre_id,
            etud["etudid"],
            code_etat=codes_cursus.ADJ,
            assidu=True,
            redirect=False,
        )
    # Vérifie que toutes les UE des étudiants notés ont été acquises:
    formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
    nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
    for etud in etuds[:5]:
        dec_ues = nt.get_etud_decisions_ue(etud["etudid"])
        for ue_id in dec_ues:
            assert dec_ues[ue_id]["code"] in {"ADM", "CMP"}

    # ---- Suppression d'un étudiant, vérification inscription
    # (permet de tester les cascades)
    etud = Identite.get_etud(etuds[0]["etudid"])
    assert etud is not None
    etudid = etud.id
    db.session.delete(etud)
    db.session.commit()
    # Vérification incription semestre:
    q = FormSemestreInscription.query.filter_by(
        etudid=etudid, formsemestre_id=formsemestre_id
    )
    assert q.count() == 0
    return formsemestre


def _signal_absences_justificatifs(etudid: int):
    etud: Identite = Identite.query.get(etudid)
    db.session.commit()
    for i in range(15, 18):
        db.session.add(
            Assiduite.create_assiduite(
                etud=etud,
                date_debut=localize_datetime(datetime.datetime(2020, 1, i, 8, 0)),
                date_fin=localize_datetime(datetime.datetime(2020, 1, i, 18, 0)),
                etat=EtatAssiduite.ABSENT,
            )
        )
    db.session.commit()
    justif: Justificatif = Justificatif.create_justificatif(
        etud,
        localize_datetime(datetime.datetime(2020, 1, 17, 8, 0)),
        localize_datetime(datetime.datetime(2020, 1, 17, 18, 0)),
        etat=EtatJustificatif.VALIDE,
    )
    db.session.add(justif)
    db.session.commit()
    justif.justifier_assiduites()