# (c) cavaliba.com - sirene - mail.py

import os
import random
import re
import time
from datetime import datetime

from celery import shared_task
from django.conf import settings
from django.core import mail
from django.template.loader import render_to_string
from django.utils import timezone

import app_home.cache as cache
from app_home.configuration import get_configuration
from app_home.log import ERROR, INFO, log


# ------------------------------------------------------------
@shared_task(ignore_result=True)
def task_send_mail(subject, text_content, dests, html_content=None, aaa=None):

    cache.init()

    sender = get_configuration("sirene", "EMAIL_FROM")
    result = sirene_send_mail(
        subject, text_content, sender, dests, html_content=html_content, aaa=aaa
    )

    return result


# ----------------------------------------------------------------------------
def mail_check_valid_address(email):

    if not email:
        return False

    if type(email) is not str:
        return False

    if not len(email) > 3:
        return False

    # regex = re.compile(r'([A-Za-z0-9]+[.-_])*[A-Za-z0-9]+@[A-Za-z0-9-]+(\.[A-Z|a-z]{2,})+')
    regex = r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,10}\b"

    if re.fullmatch(regex, email):
        return True

    return False


# ----------------------------------------------------------------------
#  sirene_html_email
# ----------------------------------------------------------------------
def sirene_html_email(message=None, updates=None):

    template = get_configuration("sirene", "EMAIL_MESSAGE_TEMPLATE")
    # relative to Django templates search path
    template_path = os.path.join("mail", template, "index.html")

    context = {
        "content": message.get_attribute_first("content"),
        "title": message.displayname,
        "severity": message.get_attribute_first("severity"),
        "category": message.get_attribute_first("category"),
        "created_by": message.get_attribute_first("created_by"),
        "created_at": message.get_attribute_first("created_at"),
    }

    if updates:
        context["updates"] = list(reversed(updates))

    # notify target
    ui_dict = message.get_dict_for_ui_detail(
        skip_external=True, skip_injected=False, skip_enumerate=False
    )
    # pprint.pprint(ui_dict)

    try:
        context["swidget"] = ui_dict["severity__widget"]["value"]
    except Exception:
        context["swidget"] = "&#x1F535;"

    r = []
    for fieldname in ui_dict.keys():
        if fieldname.startswith("notify_"):
            for target in ui_dict[fieldname]["value"]:
                # print("$$$", fieldname, ui_dict[fieldname]['value'])
                if type(target) is dict:
                    v = target.get("display", target.get("key", "?"))
                elif type(target) is list:
                    v = target[0]
                elif type(target) is str:
                    v = target
                else:
                    v = "n/a"
                if not v:
                    v = "n/a"
                r.append(v)
    context["notify_target"] = ", ".join(r)

    try:
        html_body = render_to_string(template_path, context)
    except Exception as e:
        print("Email Template error: ", e)
        html_body = message.get_attribute_first("content")

    return html_body


# ----------------------------------------------------------------------
# sirene_send_mail  : dispatch to mode
# ----------------------------------------------------------------------
def sirene_send_mail(subject, text_content, sender, dests, html_content=None, aaa=None):
    """returns  True/False"""

    mode = get_configuration("sirene", "EMAIL_MODE")

    # limit subject length
    subject_cut = subject[:80]
    if len(subject_cut) != len(subject):
        subject_cut += " (...)"

    if mode == "stdout":
        return mail_mode_stdout(subject_cut, text_content, sender, dests, html_content, aaa=aaa)

    if mode == "folder":
        return mail_mode_folder(subject_cut, text_content, sender, dests, html_content, aaa=aaa)

    if mode == "smtp":
        return mail_mode_smtp(subject_cut, text_content, sender, dests, html_content, aaa=aaa)

    return False


# ----------------------------------------------------------------------
# to STDOUT
# ----------------------------------------------------------------------
def mail_mode_stdout(subject, text_content, sender, dests, html_content=None, aaa=None):

    for dest in dests:
        if mail_check_valid_address(dest):
            print(f"MAIL (STDOUT) - {dest} - {subject}")
        else:
            log(
                ERROR,
                aaa=aaa,
                app="sirene",
                view="mail",
                action="mode_stdout",
                status="KO",
                data=f"{dest}",
            )
            return False

        log(
            INFO,
            aaa=aaa,
            app="sirene",
            view="mail",
            action="mode_stdout",
            status="OK",
            data=f"{dest} - {subject}",
        )
    return True


# ----------------------------------------------------------------------
# to Folder
# ----------------------------------------------------------------------


def mail_mode_folder(subject, text_content, sender, dests, html_content=None, aaa=None):

    now = timezone.now()
    now_msec = time.time()
    filename = str(now_msec)
    filename = str(datetime.today().strftime("%Y-%m-%d-%H%M%S"))
    filename += "-"
    alea = int(random.random() * 1000000000)
    filename += str(alea)
    filename += ".json"

    mail_folder = settings.CAVALIBA_MAIL_FOLDER
    path = os.path.join(str(mail_folder), filename)

    count = 0
    with open(path, "a") as f:
        f.write("[")
        first = True
        for dest in dests:
            if not mail_check_valid_address(dest):
                log(
                    ERROR,
                    aaa=aaa,
                    app="sirene",
                    view="mail",
                    action="mode_folder",
                    status="KO",
                    data=f"{dest}",
                )
                continue
            if not first:
                f.write(",\n")
            data = f"'timestamp': '{now}',\n'msec': {now_msec},\n"
            data += f"'from': {sender},\n"
            data += f"'to': '{dest}',\n"
            data += f"'subject': '{subject}'\n'"
            data += f"html_content': '{html_content}'\n"
            data += f"text_content': '{text_content}'\n"
            data = "{\n" + data + "}"
            f.write(data)
            count += 1
            first = False
            log(
                INFO,
                aaa=aaa,
                app="sirene",
                view="mail",
                action="mode_folder",
                status="OK",
                data=f"{dest} - {subject}",
            )

        f.write("]\n")
        f.close()

    return True


# ----------------------------------------------------------------
# SMTP
# ----------------------------------------------------------------


def mail_mode_smtp(subject, text_content, sender, dests, html_content=None, aaa=None):

    batch = []
    count = 0
    result = True
    batch_size = int(get_configuration("sirene", "EMAIL_SMTP_BATCH"))

    for dest in dests:
        if not mail_check_valid_address(dest):
            log(
                ERROR,
                aaa=aaa,
                app="sirene",
                view="mail",
                action="mode_smtp",
                status="KO",
                data=f"{dest}",
            )
            continue

        msg = mail.EmailMultiAlternatives(subject, text_content, sender, [], [dest])
        # msg = EmailMessage(subject, text_content, sender, [], [dest])

        if html_content:
            msg.attach_alternative(html_content, "text/html")

        # print(html_content)

        batch.append(msg)
        count += 1

        # ready to send ?
        if count >= batch_size:
            r2 = smtp_batch(batch, aaa=aaa)
            if not r2:
                result = False
            batch.clear()
            count = 0

    # last partial batch  ?
    if len(batch) > 0:
        r2 = smtp_batch(batch, aaa=aaa)
        if not r2:
            result = False
        batch.clear()

    if result:
        log(
            INFO,
            aaa=aaa,
            app="sirene",
            view="mail",
            action="mode_smtp",
            status="OK",
            data=f"batch - {subject}",
        )
    else:
        log(
            ERROR,
            aaa=aaa,
            app="sirene",
            view="mail",
            action="mode_smtp",
            status="KO",
            data=f"batch - {subject}",
        )

    return result


def smtp_batch(batch, aaa=None):

    try:
        connection = mail.get_connection()
    except Exception as e:
        print("Mail batch connection failed : ", e)
        log(
            ERROR,
            aaa=aaa,
            app="sirene",
            view="mail",
            action="smtp_batch",
            status="KO",
            data="Connect failed",
        )
        return False

    try:
        connection.send_messages(batch)
    except Exception as e:
        print("Mail batch send failed : ", e)
        log(
            ERROR,
            aaa=aaa,
            app="sirene",
            view="mail",
            action="smtp_batch",
            status="KO",
            data="Send failed",
        )
        # error(domain="mail.smtp.send", data=f"smtp send failed", aaa=aaa)
        # print(batch)
        return False

    try:
        connection.close()
    except Exception as e:
        print("Mail batch close connection failed : ", e)
        log(
            ERROR,
            aaa=aaa,
            app="sirene",
            view="mail",
            action="smtp_batch",
            status="KO",
            data="Close failed",
        )
        # error(domain="mail.smtp.close", data=f"smtp close connection failed", aaa=aaa)
        return False

    count = len(batch)
    log(INFO, aaa=aaa, app="sirene", view="mail", action="smtp_batch", status="OK", data=f"{count}")

    return True
