# -*- coding: UTF-8 -*

"""ScoDoc models : departements
"""
import re

from app import db
from app.models import SHORT_STR_LEN
from app.models.preferences import ScoPreference
from app.scodoc.sco_exceptions import ScoValueError

VALID_DEPT_EXP = re.compile(r"^[\w@\\\-\.]+$")


class Departement(db.Model):
    """Un département ScoDoc"""

    id = db.Column(db.Integer, primary_key=True)
    acronym = db.Column(
        db.String(SHORT_STR_LEN), nullable=False, index=True, unique=True
    )  # ne change jamais, voir la pref. DeptName
    description = db.Column(db.Text())  # pas utilisé par ScoDoc : voir DeptFullName
    date_creation = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
    visible = db.Column(
        db.Boolean(), nullable=False, default=True, server_default="true"
    )  # sur page d'accueil

    # entreprises = db.relationship("Entreprise", lazy="dynamic", backref="departement")
    etudiants = db.relationship(
        "Identite",
        back_populates="departement",
        cascade="all,delete-orphan",
        lazy="dynamic",
    )
    # This means if a Departement is deleted, all related Identite instances are also
    # deleted (all,delete). The orphan part means that if an Identite instance becomes
    # detached from its parent Departement (for example, by setting my_identite.departement = None),
    # it will be deleted.

    formations = db.relationship("Formation", lazy="dynamic", backref="departement")
    formsemestres = db.relationship(
        "FormSemestre", lazy="dynamic", backref="departement"
    )
    preferences = db.relationship(
        "ScoPreference", lazy="dynamic", backref="departement"
    )
    semsets = db.relationship("NotesSemSet", lazy="dynamic", backref="departement")
    referentiels = db.relationship(
        "ApcReferentielCompetences", lazy="dynamic", backref="departement"
    )

    def __repr__(self):
        return f"<{self.__class__.__name__}(id={self.id}, acronym='{self.acronym}')>"

    def to_dict(self, with_dept_name=True, with_dept_preferences=False):
        data = {
            "id": self.id,
            "acronym": self.acronym,
            "description": self.description,
            "visible": self.visible,
            "date_creation": self.date_creation,
        }
        if with_dept_name:
            pref = ScoPreference.query.filter_by(
                dept_id=self.id, name="DeptName"
            ).first()
            data["dept_name"] = pref.value if pref else None
        # Ceci n'est pas encore utilisé, mais pourrait être publié
        # par l'API après nettoyage des préférences.
        if with_dept_preferences:
            data["preferences"] = {
                p.name: p.value for p in ScoPreference.query.filter_by(dept_id=self.id)
            }
        return data

    @classmethod
    def invalid_dept_acronym(cls, dept_acronym: str) -> bool:
        "Check that dept_acronym is invalid"
        return (
            not dept_acronym
            or (len(dept_acronym) >= SHORT_STR_LEN)
            or not VALID_DEPT_EXP.match(dept_acronym)
        )

    @classmethod
    def from_acronym(cls, acronym):
        dept = cls.query.filter_by(acronym=acronym).first_or_404()
        return dept


def create_dept(acronym: str, visible=True) -> Departement:
    "Create new departement"
    if Departement.invalid_dept_acronym(acronym):
        raise ScoValueError("acronyme departement invalide")
    existing = Departement.query.filter_by(acronym=acronym).count()
    if existing:
        raise ScoValueError(f"acronyme {acronym} déjà existant")
    departement = Departement(acronym=acronym, visible=visible)
    p1 = ScoPreference(name="DeptName", value=acronym, departement=departement)
    db.session.add(p1)
    db.session.add(departement)
    db.session.commit()
    return departement