# -*- 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_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_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=0):
        """Enregistre une nouvelle
        Si max_frequency, ne génère pas 2 nouvelles "identiques"
         à moins de max_frequency secondes d'intervalle.
        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):
                    # on n'enregistre pas
                    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 = ModuleImpl.query.get(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 = FormSemestre.query.get(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>
        Pour être informé des évolutions de ScoDoc,
        vous pouvez vous
        <a class="stdlink" href="{scu.SCO_ANNONCES_WEBSITE}">
        abonner à la liste de diffusion</a>.
        </div>
        """
        )

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