# -*- coding: UTF-8 -*

"""Evenements et logs divers
"""
import datetime
import re

from flask import g, url_for
from flask_login import current_user

from app import db
from app import email
from app import log
from app.auth.models import User
from app.models import SHORT_STR_LEN
from app.models.moduleimpls import ModuleImpl
import app.scodoc.sco_utils as scu
from app.scodoc import sco_preferences


class Scolog(db.Model):
    """Log des actions (journal modif etudiants)"""

    __tablename__ = "scolog"

    id = db.Column(db.Integer, primary_key=True)
    date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
    method = db.Column(db.Text)
    msg = db.Column(db.Text)
    etudid = db.Column(db.Integer)  # sans contrainte pour garder logs après suppression
    authenticated_user = db.Column(db.Text)  # login, sans contrainte
    # zope_remote_addr suppressed

    @classmethod
    def logdb(
        cls, method: str = None, etudid: int = None, msg: str = None, commit=False
    ):
        """Add entry in student's log (replacement for old scolog.logdb)"""
        entry = Scolog(
            method=method,
            msg=msg,
            etudid=etudid,
            authenticated_user=current_user.user_name,
        )
        db.session.add(entry)
        if commit:
            db.session.commit()


class ScolarNews(db.Model):
    """Nouvelles pour page d'accueil"""

    NEWS_ABS = "ABS"  # saisie absence
    NEWS_APO = "APO"  # changements de codes APO
    NEWS_FORM = "FORM"  # modification formation (object=formation_id)
    NEWS_INSCR = "INSCR"  # inscription d'étudiants (object=None ou formsemestre_id)
    NEWS_JURY = "JURY"  # saisie jury
    NEWS_MISC = "MISC"  # unused
    NEWS_NOTE = "NOTES"  # saisie note (object=moduleimpl_id)
    NEWS_SEM = "SEM"  # creation semestre (object=None)

    NEWS_MAP = {
        NEWS_ABS: "saisie absence",
        NEWS_APO: "modif. code Apogée",
        NEWS_FORM: "modification formation",
        NEWS_INSCR: "inscription d'étudiants",
        NEWS_JURY: "saisie jury",
        NEWS_MISC: "opération",  # unused
        NEWS_NOTE: "saisie note",
        NEWS_SEM: "création semestre",
    }
    NEWS_TYPES = list(NEWS_MAP.keys())

    __tablename__ = "scolar_news"
    id = db.Column(db.Integer, primary_key=True)
    dept_id = db.Column(db.Integer, db.ForeignKey("departement.id"), index=True)
    date = db.Column(
        db.DateTime(timezone=True), server_default=db.func.now(), index=True
    )
    authenticated_user = db.Column(db.Text, index=True)  # login, sans contrainte
    # type in 'INSCR', 'NOTES', 'FORM', 'SEM', 'MISC'
    type = db.Column(db.String(SHORT_STR_LEN), index=True)
    object = db.Column(
        db.Integer, index=True
    )  # moduleimpl_id, formation_id, formsemestre_id
    text = db.Column(db.Text)
    url = db.Column(db.Text)

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

    def __str__(self):
        "'Chargement notes dans Stage (S3 FI) par Aurélie Dupont'"
        formsemestre = self.get_news_formsemestre()
        user = User.query.filter_by(user_name=self.authenticated_user).first()

        sem_text = (
            f"""(<a href="{url_for('notes.formsemestre_status', scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id)
            }">{formsemestre.sem_modalite()}</a>)"""
            if formsemestre
            else ""
        )
        author = f"par {user.get_nomcomplet()}" if user else ""
        return f"{self.text} {sem_text} {author}"

    def formatted_date(self) -> str:
        "06 Avr 14h23"
        mois = scu.MONTH_NAMES_ABBREV[self.date.month - 1]
        return f"{self.date.day} {mois} {self.date.hour:02d}h{self.date.minute:02d}"

    def to_dict(self):
        return {
            "date": {
                "display": self.date.strftime("%d/%m/%Y %H:%M"),
                "timestamp": self.date.timestamp(),
            },
            "type": self.NEWS_MAP.get(self.type, "?"),
            "authenticated_user": self.authenticated_user,
            "text": self.text,
        }

    @classmethod
    def last_news(cls, n=1, dept_id=None, filter_dept=True) -> list:
        "The most recent n news. Returns list of ScolarNews instances."
        query = cls.query
        if filter_dept:
            if dept_id is None:
                dept_id = g.scodoc_dept_id
            query = query.filter_by(dept_id=dept_id)

        return query.order_by(cls.date.desc()).limit(n).all()

    @classmethod
    def add(cls, typ, obj=None, text="", url=None, max_frequency=600):
        """Enregistre une nouvelle
        Si max_frequency, ne génère pas 2 nouvelles "identiques"
        à moins de max_frequency secondes d'intervalle (10 minutes par défaut).
        Deux nouvelles sont considérées comme "identiques" si elles ont
        même (obj, typ, user).
        La nouvelle enregistrée est aussi envoyée par mail.
        """
        if max_frequency:
            last_news = (
                cls.query.filter_by(
                    dept_id=g.scodoc_dept_id,
                    authenticated_user=current_user.user_name,
                    type=typ,
                    object=obj,
                )
                .order_by(cls.date.desc())
                .limit(1)
                .first()
            )
            if last_news:
                now = datetime.datetime.now(tz=last_news.date.tzinfo)
                if (now - last_news.date) < datetime.timedelta(seconds=max_frequency):
                    # pas de nouvel event, mais met à jour l'heure
                    last_news.date = datetime.datetime.now()
                    db.session.add(last_news)
                    db.session.commit()
                    return

        news = ScolarNews(
            dept_id=g.scodoc_dept_id,
            authenticated_user=current_user.user_name,
            type=typ,
            object=obj,
            text=text,
            url=url,
        )
        db.session.add(news)
        db.session.commit()
        log(f"news: {news}")
        news.notify_by_mail()

    def get_news_formsemestre(self) -> "FormSemestre":
        """formsemestre concerné par la nouvelle
        None si inexistant
        """
        from app.models.formsemestre import FormSemestre

        formsemestre_id = None
        if self.type == self.NEWS_INSCR:
            formsemestre_id = self.object
        elif self.type == self.NEWS_NOTE:
            moduleimpl_id = self.object
            if moduleimpl_id:
                modimpl = db.session.get(ModuleImpl, moduleimpl_id)
                if modimpl is None:
                    return None  # module does not exists anymore
                formsemestre_id = modimpl.formsemestre_id

        if not formsemestre_id:
            return None
        formsemestre = db.session.get(FormSemestre, formsemestre_id)
        return formsemestre

    def notify_by_mail(self):
        """Notify by email"""
        formsemestre = self.get_news_formsemestre()

        prefs = sco_preferences.SemPreferences(
            formsemestre_id=formsemestre.id if formsemestre else None
        )
        destinations = prefs["emails_notifications"] or ""
        destinations = [x.strip() for x in destinations.split(",")]
        destinations = [x for x in destinations if x]
        if not destinations:
            return
        #
        txt = self.text
        if formsemestre:
            txt += f"""\n\nSemestre {formsemestre.titre_mois()}\n\n"""
            txt += f"""<a href="{url_for("notes.formsemestre_status", _external=True,
                    scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id)
                }">{formsemestre.sem_modalite()}</a>
                """
            user = User.query.filter_by(user_name=self.authenticated_user).first()
            if user:
                txt += f"\n\nEffectué par: {user.get_nomcomplet()}\n"

        txt = (
            "\n"
            + txt
            + """\n
    --- Ceci est un message de notification automatique issu de ScoDoc
    --- vous recevez ce message car votre adresse est indiquée dans les paramètres de ScoDoc.
    """
        )

        # Transforme les URL en URL absolues
        base = scu.ScoURL()
        txt = re.sub('href=/.*?"', 'href="' + base + "/", txt)

        # Transforme les liens HTML en texte brut: '<a href="url">texte</a>' devient 'texte: url'
        # (si on veut des messages non html)
        txt = re.sub(r'<a.*?href\s*=\s*"(.*?)".*?>(.*?)</a>', r"\2: \1", txt)

        subject = "[ScoDoc] " + self.NEWS_MAP.get(self.type, "?")
        sender = email.get_from_addr()
        email.send_email(subject, sender, destinations, txt)

    @classmethod
    def scolar_news_summary_html(cls, n=5) -> str:
        """News summary, formated in HTML"""
        news_list = cls.last_news(n=n)
        if not news_list:
            return ""
        H = [
            f"""<div class="news"><span class="newstitle"><a href="{
                url_for("scolar.dept_news", scodoc_dept=g.scodoc_dept)
            }">Dernières opérations</a>
            </span><ul class="newslist">"""
        ]

        for news in news_list:
            H.append(
                f"""<li class="newslist"><span class="newsdate">{news.formatted_date()}</span><span
                class="newstext">{news}</span></li>"""
            )

        H.append("</ul>")

        # Informations générales
        H.append(
            f"""<div><a class="discretelink" href="{scu.SCO_ANNONCES_WEBSITE}">
        Pour en savoir plus sur ScoDoc voir le site scodoc.org</a>.
        </div>
        """
        )

        H.append("</div>")
        return "\n".join(H)