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

import os
import time
from datetime import datetime, timedelta
import random
import re
import pprint

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

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




# ------------------------------------------------------------
@shared_task
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 not type(email) is 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 as e:
        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"

    email_folder = get_configuration("sirene", "EMAIL_FOLDER") 

    path = email_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







