# -*- coding: UTF-8 -*

"""Model : site config  WORK IN PROGRESS #WIP
"""

from flask import flash
from app import db, log
from app.comp import bonus_spo
from app.scodoc.sco_exceptions import ScoValueError

from app.scodoc.sco_codes_parcours import (
    ADC,
    ADJ,
    ADM,
    AJ,
    ATB,
    ATJ,
    ATT,
    CMP,
    DEF,
    DEM,
    NAR,
    RAT,
)

CODES_SCODOC_TO_APO = {
    ADC: "ADMC",
    ADJ: "ADM",
    ADM: "ADM",
    AJ: "AJ",
    ATB: "AJAC",
    ATJ: "AJAC",
    ATT: "AJAC",
    CMP: "COMP",
    DEF: "NAR",
    DEM: "NAR",
    NAR: "NAR",
    RAT: "ATT",
}


def code_scodoc_to_apo_default(code):
    """Conversion code jury ScoDoc en code Apogée
    (codes par défaut, c'est configurable via ScoDocSiteConfig.get_code_apo)
    """
    return CODES_SCODOC_TO_APO.get(code, "DEF")


class ScoDocSiteConfig(db.Model):
    """Config. d'un site
    Nouveau en ScoDoc 9: va regrouper les paramètres qui dans les versions
    antérieures étaient dans scodoc_config.py
    """

    __tablename__ = "scodoc_site_config"

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(128), nullable=False, index=True)
    value = db.Column(db.Text())

    BONUS_SPORT = "bonus_sport_func_name"
    NAMES = {
        BONUS_SPORT: str,
        "always_require_ine": bool,
        "SCOLAR_FONT": str,
        "SCOLAR_FONT_SIZE": str,
        "SCOLAR_FONT_SIZE_FOOT": str,
        "INSTITUTION_NAME": str,
        "INSTITUTION_ADDRESS": str,
        "INSTITUTION_CITY": str,
        "DEFAULT_PDF_FOOTER_TEMPLATE": str,
    }

    def __init__(self, name, value):
        self.name = name
        self.value = value

    def __repr__(self):
        return f"<{self.__class__.__name__}('{self.name}', '{self.value}')>"

    @classmethod
    def get_dict(cls) -> dict:
        "Returns all data as a dict name = value"
        return {
            c.name: cls.NAMES.get(c.name, lambda x: x)(c.value)
            for c in ScoDocSiteConfig.query.all()
        }

    @classmethod
    def set_bonus_sport_class(cls, class_name):
        """Record bonus_sport config.
        If class_name not defined, raise NameError
        """
        if class_name not in cls.get_bonus_sport_class_names():
            raise NameError("invalid class name for bonus_sport")
        c = ScoDocSiteConfig.query.filter_by(name=cls.BONUS_SPORT).first()
        if c:
            log("setting to " + class_name)
            c.value = class_name
        else:
            c = ScoDocSiteConfig(cls.BONUS_SPORT, class_name)
        db.session.add(c)
        db.session.commit()

    @classmethod
    def get_bonus_sport_class_name(cls):
        """Get configured bonus function name, or None if None."""
        klass = cls.get_bonus_sport_class_from_name()
        if klass is None:
            return ""
        else:
            return klass.name

    @classmethod
    def get_bonus_sport_class(cls):
        """Get configured bonus function, or None if None."""
        return cls.get_bonus_sport_class_from_name()

    @classmethod
    def get_bonus_sport_class_from_name(cls, class_name=None):
        """returns bonus class with specified name.
        If name not specified, return the configured function.
        None if no bonus function configured.
        If class_name not found in module bonus_sport, returns None
        and flash a warning.
        """
        if not class_name:  # None or ""
            c = ScoDocSiteConfig.query.filter_by(name=cls.BONUS_SPORT).first()
            if c is None:
                return None
            class_name = c.value
        if class_name == "":  # pas de bonus défini
            return None
        klass = bonus_spo.get_bonus_class_dict().get(class_name)
        if klass is None:
            flash(
                f"""Fonction de calcul bonus sport inexistante: {class_name}. 
                Changez là ou contactez votre administrateur local."""
            )
        return klass

    @classmethod
    def get_bonus_sport_class_names(cls) -> list:
        """List available bonus class names
        (starting with empty string to represent "no bonus function").
        """
        return [""] + sorted(bonus_spo.get_bonus_class_dict().keys())

    @classmethod
    def get_bonus_sport_class_list(cls) -> list[tuple]:
        """List available bonus class names
        (starting with empty string to represent "no bonus function").
        """
        d = bonus_spo.get_bonus_class_dict()
        class_list = [(name, d[name].displayed_name) for name in d.keys()]
        class_list.sort(key=lambda x: x[1].replace(" du ", " de "))
        return [("", "")] + class_list

    @classmethod
    def get_bonus_sport_func(cls):
        """Fonction bonus_sport ScoDoc 7 XXX
        Transitoire pour les tests durant la transition #sco92
        """
        """returns bonus func with specified name.
        If name not specified, return the configured function.
        None if no bonus function configured.
        Raises ScoValueError if func_name not found in module bonus_sport.
        """
        from app.scodoc import bonus_sport

        c = ScoDocSiteConfig.query.filter_by(name=cls.BONUS_SPORT).first()
        if c is None:
            return None
        func_name = c.value
        if func_name == "":  # pas de bonus défini
            return None
        try:
            return getattr(bonus_sport, func_name)
        except AttributeError:
            raise ScoValueError(
                f"""Fonction de calcul de l'UE bonus inexistante: "{func_name}". 
                (contacter votre administrateur local)."""
            )

    @classmethod
    def get_code_apo(cls, code: str) -> str:
        """La représentation d'un code pour les exports Apogée.
        Par exemple, à l'iUT du H., le code ADM est réprésenté par VAL
        Les codes par défaut sont donnés dans sco_apogee_csv.

        """
        cfg = ScoDocSiteConfig.query.filter_by(name=code).first()
        if not cfg:
            code_apo = code_scodoc_to_apo_default(code)
        else:
            code_apo = cfg.value
        return code_apo

    @classmethod
    def set_code_apo(cls, code: str, code_apo: str):
        """Enregistre nouvelle représentation du code"""
        if code_apo != cls.get_code_apo(code):
            cfg = ScoDocSiteConfig.query.filter_by(name=code).first()
            if cfg is None:
                cfg = ScoDocSiteConfig(code, code_apo)
            else:
                cfg.value = code_apo
            db.session.add(cfg)
            db.session.commit()
