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

"""Creation environnement pour test.
A utiliser avec debug.py (côté serveur).

La classe ScoFake offre un ensemble de raccourcis permettant d'écrire 
facilement des tests ou de reproduire des bugs.
"""

from __future__ import print_function
from functools import wraps
import sys
import string
import collections
import pprint
import random

import scodoc_manager
from app.scodoc import notesdb as ndb
from app.scodoc import sco_codes_parcours
from app.scodoc import sco_edit_formation
from app.scodoc import sco_edit_matiere
from app.scodoc import sco_edit_module
from app.scodoc import sco_edit_ue
from app.scodoc import sco_etud
from app.scodoc import sco_evaluations
from app.scodoc import sco_formations
from app.scodoc import sco_formsemestre
from app.scodoc import sco_formsemestre_inscriptions
from app.scodoc import sco_formsemestre_validation
from app.scodoc import sco_moduleimpl
from app.scodoc import sco_saisie_notes
from app.scodoc import sco_synchro_etuds
from app.scodoc import sco_utils as scu
from app.scodoc.debug import REQUEST
from app.scodoc.notes_log import log
from app.scodoc.sco_exceptions import ScoValueError

random.seed(12345)  # tests reproductibles


DEMO_DIR = scu.SCO_SRC_DIR + "/scotests/demo/"
NOMS = [x.strip() for x in open(DEMO_DIR + "/noms.txt").readlines()]
PRENOMS_H = [x.strip() for x in open(DEMO_DIR + "/prenoms-h.txt").readlines()]
PRENOMS_F = [x.strip() for x in open(DEMO_DIR + "/prenoms-f.txt").readlines()]
PRENOMS_X = [x.strip() for x in open(DEMO_DIR + "/prenoms-x.txt").readlines()]

# nb: en python2, les chaines ci-dessus sont en utf8


def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
    return "".join(random.choice(chars) for _ in range(size))


def logging_meth(func):
    @wraps(func)
    def wrapper_logging_meth(self, *args, **kwargs):
        r = func(self, *args, **kwargs)
        self.log("%s(%s) -> \n%s" % (func.__name__, kwargs, pprint.pformat(r)))
        return r

    return wrapper_logging_meth


class ScoFake:
    def __init__(self, context, verbose=True):
        self.context = context
        self.verbose = verbose

    def log(self, msg):
        if self.verbose:
            print("ScoFake: " + str(msg), file=sys.stderr)
            sys.stderr.flush()
        log("ScoFake: " + str(msg))

    def civilitenomprenom(self):
        """un nom et un prenom au hasard,
        toujours en majuscules.
        """
        civilite = random.choice(("M", "M", "M", "F", "F", "F", "X"))
        if civilite == "F":
            prenom = random.choice(PRENOMS_F)
        elif civilite == "M":
            prenom = random.choice(PRENOMS_H)
        elif civilite == "X":
            prenom = random.choice(PRENOMS_X)
        else:
            raise ValueError("invalid civilite value")
        return civilite, random.choice(NOMS).upper(), prenom.upper()

    @logging_meth
    def create_etud(
        self,
        cnx=None,
        code_nip="",
        nom="",
        prenom="",
        code_ine="",
        civilite="",
        etape="TST1",
        email="test@localhost",
        emailperso="perso@localhost",
        date_naissance="01/01/2001",
        lieu_naissance="Paris",
        dept_naissance="75",
        domicile="1, rue du test",
        codepostaldomicile="75123",
        villedomicile="TestCity",
        paysdomicile="France",
        telephone="0102030405",
        typeadresse="domicile",
        boursier=None,
        description="etudiant test",
    ):
        """Crée un étudiant"""
        if not cnx:
            cnx = ndb.GetDBConnexion()
        if code_nip == "":
            code_nip = str(random.randint(10000, 99999))
        if not civilite or not nom or not prenom:
            r_civilite, r_nom, r_prenom = self.civilitenomprenom()
            if not civilite:
                civilite = r_civilite
            if not nom:
                nom = r_nom
            if not prenom:
                prenom = r_prenom
        etud = sco_etud.create_etud(self.context, cnx, args=locals(), REQUEST=REQUEST)
        inscription = "2020"  # pylint: disable=unused-variable
        sco_synchro_etuds.do_import_etud_admission(
            self.context, cnx, etud["etudid"], locals()
        )
        return etud

    @logging_meth
    def create_formation(
        self,
        acronyme="test",
        titre="Formation test",
        titre_officiel="Le titre officiel de la formation test",
        type_parcours=sco_codes_parcours.ParcoursDUT.TYPE_PARCOURS,
        formation_code=None,
        code_specialite=None,
    ):
        """Crée une formation"""
        if not acronyme:
            acronyme = "TEST" + str(random.randint(100000, 999999))
        oid = sco_edit_formation.do_formation_create(
            self.context, locals(), REQUEST=REQUEST
        )
        oids = sco_formations.formation_list(self.context, formation_id=oid)
        if not oids:
            raise ScoValueError("formation not created !")
        return oids[0]

    @logging_meth
    def create_ue(
        self,
        formation_id=None,
        acronyme=None,
        numero=None,
        titre="",
        type=None,
        ue_code=None,
        ects=None,
        is_external=None,
        code_apogee=None,
        coefficient=None,
    ):
        """Crée une UE"""
        if numero is None:
            numero = sco_edit_ue.next_ue_numero(self.context, formation_id, 0)
        oid = sco_edit_ue.do_ue_create(self.context, locals(), REQUEST)
        oids = sco_edit_ue.do_ue_list(self.context, args={"ue_id": oid})
        if not oids:
            raise ScoValueError("ue not created !")
        return oids[0]

    @logging_meth
    def create_matiere(self, ue_id=None, titre=None, numero=None):
        oid = sco_edit_matiere.do_matiere_create(self.context, locals(), REQUEST)
        oids = sco_edit_matiere.do_matiere_list(self.context, args={"matiere_id": oid})
        if not oids:
            raise ScoValueError("matiere not created !")
        return oids[0]

    @logging_meth
    def create_module(
        self,
        titre=None,
        code=None,
        heures_cours=None,
        heures_td=None,
        heures_tp=None,
        coefficient=None,
        ue_id=None,
        formation_id=None,
        matiere_id=None,
        semestre_id=1,
        numero=None,
        abbrev=None,
        ects=None,
        code_apogee=None,
        module_type=None,
    ):
        oid = sco_edit_module.do_module_create(self.context, locals(), REQUEST)
        oids = sco_edit_module.do_module_list(self.context, args={"module_id": oid})
        if not oids:
            raise ScoValueError("module not created ! (oid=%s)" % oid)
        return oids[0]

    @logging_meth
    def create_formsemestre(
        self,
        formation_id=None,
        semestre_id=None,
        titre=None,
        date_debut=None,
        date_fin=None,
        etat=None,
        gestion_compensation=None,
        bul_hide_xml=None,
        gestion_semestrielle=None,
        bul_bgcolor=None,
        modalite=None,
        resp_can_edit=None,
        resp_can_change_ens=None,
        ens_can_edit_eval=None,
        elt_sem_apo=None,
        elt_annee_apo=None,
        etapes=None,
        responsables=["bach"],
    ):
        oid = sco_formsemestre.do_formsemestre_create(self.context, locals(), REQUEST)
        # oids = self.context.do_formsemestre_list(args={"formsemestre_id": oid})
        oids = sco_formsemestre.do_formsemestre_list(
            self.context, args={"formsemestre_id": oid}
        )  # API inconsistency
        if not oids:
            raise ScoValueError("formsemestre not created !")
        return oids[0]

    @logging_meth
    def create_moduleimpl(
        self,
        module_id=None,
        formsemestre_id=None,
        responsable_id=None,
    ):
        oid = sco_moduleimpl.do_moduleimpl_create(self.context, locals())
        oids = sco_moduleimpl.do_moduleimpl_list(
            self.context, moduleimpl_id=oid
        )  # API inconsistency
        if not oids:
            raise ScoValueError("moduleimpl not created !")
        return oids[0]

    @logging_meth
    def inscrit_etudiant(self, sem, etud):
        sco_formsemestre_inscriptions.do_formsemestre_inscription_with_modules(
            self.context,
            sem["formsemestre_id"],
            etud["etudid"],
            etat="I",
            etape=etud.get("etape", None),
            REQUEST=REQUEST,
            method="test_inscrit_etudiant",
        )

    @logging_meth
    def create_evaluation(
        self,
        moduleimpl_id=None,
        jour=None,
        heure_debut="8h00",
        heure_fin="9h00",
        description=None,
        note_max=20,
        coefficient=None,
        visibulletin=None,
        publish_incomplete=None,
        evaluation_type=None,
        numero=None,
        REQUEST=REQUEST,
    ):
        args = locals()
        del args["self"]
        oid = sco_evaluations.do_evaluation_create(self.context, **args)
        oids = sco_evaluations.do_evaluation_list(
            self.context, args={"evaluation_id": oid}
        )
        if not oids:
            raise ScoValueError("evaluation not created !")
        return oids[0]

    @logging_meth
    def create_note(
        self,
        evaluation=None,
        etud=None,
        note=None,
        comment=None,
        uid="bach",
    ):
        return sco_saisie_notes._notes_add(
            self.context,
            uid,
            evaluation["evaluation_id"],
            [(etud["etudid"], note)],
            comment=comment,
        )

    def setup_formation(
        self,
        nb_semestre=1,
        nb_ue_per_semestre=2,
        nb_module_per_ue=2,
        acronyme=None,
        titre=None,
    ):
        """Création d'une formation, avec UE, modules et évaluations.

        Formation avec `nb_semestre` comportant chacun `nb_ue_per_semestre` UE
        et dans chaque UE `nb_module_per_ue` modules (on a une seule matière par UE).

        Returns:
            formation (dict), liste d'ue (dicts), liste de modules.
        """
        f = self.create_formation(acronyme=acronyme, titre=titre)
        ue_list = []
        mod_list = []
        for semestre_id in range(1, nb_semestre + 1):
            for n in range(1, nb_ue_per_semestre + 1):
                ue = self.create_ue(
                    formation_id=f["formation_id"],
                    acronyme="TSU%s%s" % (semestre_id, n),
                    titre="ue test %s%s" % (semestre_id, n),
                )
                ue_list.append(ue)
                mat = self.create_matiere(ue_id=ue["ue_id"], titre="matière test")
                for _ in range(nb_module_per_ue):
                    mod = self.create_module(
                        matiere_id=mat["matiere_id"],
                        semestre_id=semestre_id,
                        code="TSM%s" % len(mod_list),
                        coefficient=1.0,
                        titre="module test",
                        ue_id=ue["ue_id"],  # faiblesse de l'API
                        formation_id=f["formation_id"],  # faiblesse de l'API
                    )
                    mod_list.append(mod)
        return f, ue_list, mod_list

    def setup_formsemestre(
        self,
        f,
        mod_list,
        semestre_id=1,
        date_debut="01/01/2020",
        date_fin="30/06/2020",
        nb_evaluations_per_module=1,
        titre=None,
        responsables=["bach"],
        modalite=None,
    ):
        """Création semestre, avec modules et évaluations."""
        sem = self.create_formsemestre(
            formation_id=f["formation_id"],
            semestre_id=semestre_id,
            date_debut=date_debut,
            date_fin=date_fin,
            titre=titre,
            responsables=responsables,
            modalite=modalite,
        )
        eval_list = []
        for mod in mod_list:
            if mod["semestre_id"] == semestre_id:
                mi = self.create_moduleimpl(
                    module_id=mod["module_id"],
                    formsemestre_id=sem["formsemestre_id"],
                    responsable_id="bach",
                )
                for e_idx in range(1, nb_evaluations_per_module + 1):
                    e = self.create_evaluation(
                        moduleimpl_id=mi["moduleimpl_id"],
                        jour=date_debut,
                        description="evaluation test %s" % e_idx,
                        coefficient=1.0,
                    )
                    eval_list.append(e)
        return sem, eval_list

    def set_etud_notes_sem(
        self, sem, eval_list, etuds, notes=None, random_min=0, random_max=20
    ):
        """Met des notes aux étudiants indiqués des evals indiquées.

        Args:
            sem: dict
            eval_list: list of dicts
            etuds: list of dicts
            notes: liste des notes (float).
            Si non spécifié, tire au hasard dans `[random_min, random_max]`
        """
        set_random = notes is None
        for e in eval_list:
            if set_random:
                notes = [float(random.randint(random_min, random_max)) for _ in etuds]
            for etud, note in zip(etuds, notes):
                self.create_note(evaluation=e, etud=etud, note=note)

    def set_code_jury(
        self,
        sem,
        etud,
        code_etat=sco_codes_parcours.ADM,
        devenir=sco_codes_parcours.NEXT,
        assidu=True,
    ):
        """Affecte décision de jury"""
        sco_formsemestre_validation.formsemestre_validation_etud_manu(
            self.context,
            formsemestre_id=sem["formsemestre_id"],
            etudid=etud["etudid"],
            code_etat=code_etat,
            devenir=devenir,
            assidu=assidu,
            REQUEST=REQUEST,
        )


# band aid for #sco8 dev (temporaire en attendant Users)
class Sco8Context(object):
    pass