from smtplib import SMTPRecipientsRefused
from typing import Optional

import structlog
from django.conf import settings
from django.core import mail
from django.template.context import Context
from django_q.tasks import async_task

from greg.models import InvitationLink, Sponsor
from gregui.models import EmailTemplate

logger = structlog.getLogger(__name__)


class InviteGuest:
    def make_registration_url(self, token: str) -> str:
        return "{base}/invite#{token}".format(
            base=settings.BASE_URL,
            token=token,
        )

    def get_template(self):
        return EmailTemplate.objects.get(
            template_key=EmailTemplate.EmailType.GUEST_REGISTRATION
        )

    def get_notice_template(self):
        return EmailTemplate.objects.get(
            template_key=EmailTemplate.EmailType.INVALID_EMAIL
        )

    def send_bad_email_notice(
        self, guest_name: str, guest_mail: str, sponsor_mail: str
    ):
        template = self.get_notice_template()
        context = Context(
            {
                "guest_name": guest_name,
                "guest_email": guest_mail,
            }
        )
        mail.send_mail(
            subject=template.get_subject(context),
            message=template.get_body(context),
            from_email=template.from_email or None,
            recipient_list=[sponsor_mail],
        )

    def queue_mail(self, link: InvitationLink) -> Optional[str]:
        guest = link.invitation.role.person
        email_address = guest.private_email
        if not email_address:
            logger.warning(
                "No e-mail address found for invitation link with ID: {%s}", link.id
            )
            return None
        sponsor = link.invitation.role.sponsor
        return async_task(
            "gregui.mailutils.invite_guest.try_send_registration_mail",
            **{
                "guest_name": f"{guest.first_name} {guest.last_name}",
                "guest_mail": email_address.value,
                "sponsor_id": sponsor.id,
                "sponsor_name": f"{sponsor.first_name} {sponsor.last_name}",
                "token": link.uuid,
            },
        )


def try_send_registration_mail(
    guest_name: str,
    guest_mail: str,
    sponsor_id: int,
    sponsor_name: str,
    token: str,
):
    ig = InviteGuest()
    template = ig.get_template()
    context = Context(
        {
            "institution": settings.INSTANCE_NAME,
            "sponsor": sponsor_name,
            "registration_link": ig.make_registration_url(token),
        }
    )
    try:
        mail.send_mail(
            subject=template.get_subject(context),
            message=template.get_body(context),
            from_email=template.from_email or None,
            recipient_list=[guest_mail],
        )
    except SMTPRecipientsRefused:
        sp = Sponsor.objects.get(pk=sponsor_id)
        # TODO: Mark the invite in the frontend as bad
        if sp.work_email:
            ig.send_bad_email_notice(
                guest_name=guest_name,
                guest_mail=guest_mail,
                sponsor_mail=sp.work_email,
            )