from __future__ import annotations

import time
from email.utils import make_msgid
from smtplib import SMTPDataError, SMTPServerDisconnected
from threading import Thread

from django.conf import settings
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string as render


class EmailThread(Thread):
    MAX_TRIES = 3

    def __init__(self, message):
        Thread.__init__(self)
        self.message = message

    def run(self):
        for attempt in range(0, self.MAX_TRIES):
            try:
                # Make sure each retry creates a new connection:
                self.message.connection = None
                self.message.send()
                # No exception--great! Return from the retry loop
                return
            except (SMTPServerDisconnected, SMTPDataError) as e:
                if attempt + 1 == self.MAX_TRIES:
                    # This was the last attempt and it failed:
                    # re-raise the exception
                    raise e

                # Wait 1s before retrying
                time.sleep(1)


def make_message(name, to, ctx, headers={}):
    subject = render("emails/%s-subject.html" % name, ctx).strip()
    body = render("emails/%s-body-text.html" % name, ctx)
    html = render("emails/%s-body-html.html" % name, ctx)

    domain = settings.DEFAULT_FROM_EMAIL.split("@")[-1].strip(">")
    headers["Message-ID"] = make_msgid(domain=domain)

    # Make sure the From: header contains our display From: address
    if "From" not in headers:
        headers["From"] = settings.DEFAULT_FROM_EMAIL

    # If EMAIL_MAIL_FROM_TMPL is set, prepare a custom MAIL FROM address
    bounce_id = headers.pop("X-Bounce-ID", "bounces")
    if settings.EMAIL_MAIL_FROM_TMPL:
        from_email = settings.EMAIL_MAIL_FROM_TMPL % bounce_id
    else:
        from_email = settings.DEFAULT_FROM_EMAIL

    msg = EmailMultiAlternatives(
        subject, body, from_email=from_email, to=(to,), headers=headers
    )
    msg.attach_alternative(html, "text/html")
    return msg


def send(msg, block=False):
    assert settings.EMAIL_HOST, (
        "No SMTP configuration,"
        " see https://github.com/healthchecks/healthchecks#sending-emails"
    )

    t = EmailThread(msg)
    if block or hasattr(settings, "BLOCKING_EMAILS"):
        # In tests, we send emails synchronously
        # so we can inspect the outgoing messages
        t.run()
    else:
        # Outside tests, we send emails on thread,
        # so there is no delay for the user.
        t.start()


def login(to, ctx):
    send(make_message("login", to, ctx))


def transfer_request(to, ctx):
    send(make_message("transfer-request", to, ctx))


def alert(to, ctx, headers={}):
    send(make_message("alert", to, ctx, headers=headers))


def verify_email(to, ctx):
    send(make_message("verify-email", to, ctx))


def report(to, ctx, headers={}):
    m = make_message("report", to, ctx, headers=headers)
    send(m, block=True)


def deletion_notice(to, ctx, headers={}):
    m = make_message("deletion-notice", to, ctx, headers=headers)
    send(m, block=True)


def deletion_scheduled(to, ctx, headers={}):
    m = make_message("deletion-scheduled", to, ctx, headers=headers)
    send(m, block=True)


def sms_limit(to, ctx):
    send(make_message("sms-limit", to, ctx))


def call_limit(to, ctx):
    send(make_message("phone-call-limit", to, ctx))


def sudo_code(to, ctx):
    send(make_message("sudo-code", to, ctx))


def signal_rate_limited(to, ctx):
    send(make_message("signal-rate-limited", to, ctx))