"""Test models referentiel compétences

Utiliser par exemple comme: 
    pytest tests/unit/test_refcomp.py

"""

from flask import g

from app import db
from app import models
from app.but.import_refcomp import orebut_import_refcomp
from app.models import UniteEns
from app.models.but_refcomp import (
    ApcReferentielCompetences,
    ApcCompetence,
    ApcSituationPro,
    ApcNiveau,
)
from app.models.formations import Formation

from tests.unit import setup

REF_RT_XML = open(
    "ressources/referentiels/but2022/competences/but-RT-05012022-081735.xml"
).read()
REF_MLT_XML = open(
    "ressources/referentiels/but2022/competences/but-MLT-05012022-081603.xml"
).read()
REF_GCCD_XML = open(
    "ressources/referentiels/but2022/competences/but-GCCD-05012022-081630.xml"
).read()
REF_INFO_XML = open(
    "ressources/referentiels/but2022/competences/but-INFO-05012022-081701.xml"
).read()


def test_but_refcomp(test_client):
    """modèles ref. comp."""
    dept_id = models.Departement.query.first().id
    ref_comp: ApcReferentielCompetences = orebut_import_refcomp(REF_RT_XML, dept_id)
    assert ref_comp.competences.count() == 13
    assert ref_comp.competences[0].situations.count() == 3
    assert ref_comp.competences[0].situations[0].libelle.startswith("Conception ")
    competences = ref_comp.competences.all()
    assert (
        competences[-1].situations.all()[-1].libelle
        == "Administration des services multimédia"
    )
    # test cascades on delete
    db.session.delete(ref_comp)
    db.session.commit()
    assert ApcCompetence.query.count() == 0
    assert ApcSituationPro.query.count() == 0


def test_but_assoc_ue_parcours(test_client):
    """Association UE / Niveau compétence"""
    dept_id = models.Departement.query.first().id
    G, formation_id, (ue1_id, ue2_id, ue3_id), module_ids = setup.build_formation_test()
    ref_comp: ApcReferentielCompetences = orebut_import_refcomp(REF_RT_XML, dept_id)
    ue = db.session.get(UniteEns, ue1_id)
    assert ue.niveau_competence is None
    niveau = ApcNiveau.query.first()
    ue.niveau_competence = niveau
    db.session.add(ue)
    db.session.commit()
    ue = db.session.get(UniteEns, ue1_id)
    assert ue.niveau_competence == niveau
    assert len(niveau.ues) == 1
    assert niveau.ues[0] == ue


def test_but_assoc_refcomp(test_client):
    """Association formation / référentiel de compétences"""
    dept_id = models.Departement.query.first().id
    G, formation_id, (ue1_id, ue2_id, ue3_id), module_ids = setup.build_formation_test()
    formation: Formation = db.session.get(Formation, formation_id)
    assert formation is not None
    ref_comp: ApcReferentielCompetences = orebut_import_refcomp(REF_RT_XML, dept_id)
    formation.referentiel_competence_id = ref_comp.id
    db.session.add(formation)
    db.session.commit()
    ue = db.session.get(UniteEns, ue1_id)
    niveau = (
        ApcNiveau.query.filter_by(annee="BUT1")
        .join(ApcCompetence)
        .filter_by(referentiel_id=ref_comp.id)
        .first()
    )
    assert niveau is not None
    ue.niveau_competence_id = niveau.id
    db.session.add(ue)
    db.session.commit()
    formation.refcomp_desassoc()
    assert ue.niveau_competence_id is None


def test_refcomp_niveaux_mlt(test_client):
    """Test calcul niveaux / parcours pour un BUT MLT
    avec en parcours "Mobilité et Supply Chain Connectées"
    une compétence "Transporter" sur BUT1, BUT2
    et une compétence "Digitaliser" sur BUT2, BUT3
    """
    # Charger le ref. comp. MLT
    dept_id = models.Departement.query.first().id
    ref_comp: ApcReferentielCompetences = orebut_import_refcomp(REF_MLT_XML, dept_id)
    # Vérifier qu'on a 2 parcours et 4 compétences dans chaque
    nb_parcours = ref_comp.parcours.count()
    assert nb_parcours == 2
    assert [len(p.query_competences().all()) for p in ref_comp.parcours] == [
        4
    ] * nb_parcours
    # 3 niveaux en 1ere année (dans chaque parcours):
    assert [
        len(ApcNiveau.niveaux_annee_de_parcours(p, 1, ref_comp))
        for p in ref_comp.parcours
    ] == [3, 3]
    # 2 niveaux en 2ème année (dans chaque parcours):
    assert [
        len(ApcNiveau.niveaux_annee_de_parcours(p, 2, ref_comp))
        for p in ref_comp.parcours
    ] == [4, 4]
    # 3 niveaux en 3ème année (dans chaque parcours):
    assert [
        len(ApcNiveau.niveaux_annee_de_parcours(p, 3, ref_comp))
        for p in ref_comp.parcours
    ] == [3, 3]
    # Vérifier les niveaux_by_parcours
    parcour = ref_comp.parcours.first()
    # BUT 1
    parcours, niveaux_by_parcours = ref_comp.get_niveaux_by_parcours(
        1, [parcour] if parcour else None
    )
    assert parcours == [parcour]  # le parcours indiqué
    assert (tuple(niveaux_by_parcours.keys())) == (parcour.id, "TC")
    assert niveaux_by_parcours[parcour.id] == []  # tout en tronc commun en BUT1 MLT
    assert niveaux_by_parcours["TC"][0].competence.titre == "Transporter"
    assert len(niveaux_by_parcours["TC"]) == 3
    # BUT 2
    parcours, niveaux_by_parcours = ref_comp.get_niveaux_by_parcours(
        2, [parcour] if parcour else None
    )
    assert parcours == [parcour]  # le parcours indiqué
    assert (tuple(niveaux_by_parcours.keys())) == (parcour.id, "TC")
    assert len(niveaux_by_parcours[parcour.id]) == 1
    assert len(niveaux_by_parcours["TC"]) == 3
    # BUT 3
    parcours, niveaux_by_parcours = ref_comp.get_niveaux_by_parcours(
        3, [parcour] if parcour else None
    )
    assert parcours == [parcour]  # le parcours indiqué
    assert (tuple(niveaux_by_parcours.keys())) == (parcour.id, "TC")
    assert len(niveaux_by_parcours[parcour.id]) == 1
    assert len(niveaux_by_parcours["TC"]) == 2
    # Vérifier les niveaux de chaque année
    assert len(ApcNiveau.niveaux_annee_de_parcours(parcour, 1)) == 3
    assert len(ApcNiveau.niveaux_annee_de_parcours(parcour, 2)) == 4
    assert len(ApcNiveau.niveaux_annee_de_parcours(parcour, 3)) == 3


def test_refcomp_niveaux_gccd(test_client):
    """Test calcul niveaux / parcours pour un BUT GCCD
    avec en parcours "Travaux Bâtiment" 5 compétences
    dont la première "Solutions Bâtiment" sur BUT1, BUT2, BUT3
    et en parcours "Travaux Publics" la même sur BUT1, BUT2 seulement.
    """
    # Charger le ref. comp. GCCD
    dept_id = models.Departement.query.first().id
    ref_comp: ApcReferentielCompetences = orebut_import_refcomp(REF_GCCD_XML, dept_id)
    # Vérifier qu'on a 4 parcours et 5 compétences dans chaque
    nb_parcours = ref_comp.parcours.count()
    assert nb_parcours == 4
    assert [len(p.query_competences().all()) for p in ref_comp.parcours] == [
        5
    ] * nb_parcours
    # 5 niveaux en 1ere année (dans chaque parcours):
    assert [
        len(ApcNiveau.niveaux_annee_de_parcours(p, 1, ref_comp))
        for p in ref_comp.parcours
    ] == [5] * nb_parcours
    # 5 niveaux en 2ème année (dans chaque parcours):
    assert [
        len(ApcNiveau.niveaux_annee_de_parcours(p, 2, ref_comp))
        for p in ref_comp.parcours
    ] == [5] * nb_parcours
    # 3 niveaux en 3ème année (dans chaque parcours):
    assert [
        len(ApcNiveau.niveaux_annee_de_parcours(p, 3, ref_comp))
        for p in ref_comp.parcours
    ] == [3] * nb_parcours
    # Vérifier les niveaux_by_parcours
    parcour = ref_comp.parcours.first()
    # BUT 1
    parcours, niveaux_by_parcours = ref_comp.get_niveaux_by_parcours(
        1, [parcour] if parcour else None
    )
    assert parcours == [parcour]  # le parcours indiqué
    assert (tuple(niveaux_by_parcours.keys())) == (parcour.id, "TC")
    assert len(niveaux_by_parcours[parcour.id]) == 0
    assert len(niveaux_by_parcours["TC"]) == 5
    # BUT 3
    parcours, niveaux_by_parcours = ref_comp.get_niveaux_by_parcours(
        3, [parcour] if parcour else None
    )
    assert parcours == [parcour]  # le parcours indiqué
    assert (tuple(niveaux_by_parcours.keys())) == (parcour.id, "TC")
    assert len(niveaux_by_parcours[parcour.id]) == 3
    assert len(niveaux_by_parcours["TC"]) == 0
    # Vérifier les niveaux de chaque année
    assert len(ApcNiveau.niveaux_annee_de_parcours(parcour, 1)) == 5
    assert len(ApcNiveau.niveaux_annee_de_parcours(parcour, 2)) == 5
    assert len(ApcNiveau.niveaux_annee_de_parcours(parcour, 3)) == 3