# pylint: disable=line-too-long

"""A minimalist client to synchronize with mailbox provisioning API."""

import json
import smtplib
from email.errors import HeaderParseError, NonASCIILocalPartDefect
from email.headerregistry import Address
from logging import getLogger

from django.conf import settings
from django.contrib.auth.hashers import make_password
from django.contrib.sites.models import Site
from django.core import exceptions, mail
from django.template.loader import render_to_string
from django.utils.translation import gettext_lazy as _
from django.utils.translation import override

import requests
from rest_framework import status
from urllib3.util import Retry

from mailbox_manager import enums, models

logger = getLogger(__name__)

adapter = requests.adapters.HTTPAdapter(
    max_retries=Retry(
        total=4,
        backoff_factor=0.1,
        status_forcelist=[500, 502],
        allowed_methods=["PATCH"],
    )
)

session = requests.Session()
session.mount("http://", adapter)
session.mount("https://", adapter)


class DimailAPIClient:
    """A dimail-API client."""

    API_URL = settings.MAIL_PROVISIONING_API_URL
    API_CREDENTIALS = settings.MAIL_PROVISIONING_API_CREDENTIALS
    API_TIMEOUT = settings.MAIL_PROVISIONING_API_TIMEOUT

    def _get_headers(self):
        """
        Return Bearer token. Requires MAIL_PROVISIONING_API_CREDENTIALS setting,
        to get a token from dimail /token/ endpoint.
        """

        try:
            response = requests.get(
                f"{self.API_URL}/token/",
                headers={"Authorization": f"Basic {self.API_CREDENTIALS}"},
                timeout=self.API_TIMEOUT,
                allow_redirects=True,
            )
        except requests.exceptions.ConnectionError as error:
            logger.error(
                "Connection error while trying to reach %s.",
                self.API_URL,
                exc_info=error,
            )
            raise error

        if response.status_code == status.HTTP_200_OK:
            headers = {
                "Content-Type": "application/json",
                "Authorization": f"Bearer {response.json()['access_token']}",
            }
            logger.info("Token successfully granted by mail-provisioning API.")
            return headers

        if response.status_code == status.HTTP_403_FORBIDDEN:
            logger.error(
                "[DIMAIL] 403 Forbidden: Could not retrieve a token,"
                "please check 'MAIL_PROVISIONING_API_CREDENTIALS' setting.",
            )
            raise exceptions.PermissionDenied(
                "Token denied. Please check your MAIL_PROVISIONING_API_CREDENTIALS."
            )

        return self._raise_exception_for_unexpected_response(response)

    def create_domain(self, domain_name, request_user):
        """Send a domain creation request to dimail API."""

        payload = {
            "name": domain_name,
            "context_name": domain_name,  # for now, we put each domain on its own context
            "features": ["webmail", "mailbox", "alias"],
            "delivery": "virtual",
        }
        try:
            response = session.post(
                f"{self.API_URL}/domains/",
                json=payload,
                headers={"Authorization": f"Basic {self.API_CREDENTIALS}"},
                verify=True,
                timeout=self.API_TIMEOUT,
                allow_redirects=True,
            )
        except requests.exceptions.ConnectionError as error:
            logger.error(
                "Connection error while trying to reach %s.",
                self.API_URL,
                exc_info=error,
            )
            raise error

        if response.status_code == status.HTTP_201_CREATED:
            logger.info(
                "Domain %s successfully created on dimail by user %s",
                domain_name,
                request_user,
            )
            return response

        return self._raise_exception_for_unexpected_response(response)

    def create_mailbox(self, mailbox, request_user=None):
        """Send a CREATE mailbox request to mail provisioning API."""

        payload = {
            # givenName value can be empty
            "givenName": mailbox.first_name,
            # surName value can be empty
            "surName": mailbox.last_name,
            # displayName value has to be unique
            "displayName": f"{mailbox.first_name} {mailbox.last_name}",
        }
        headers = self._get_headers()

        try:
            response = session.post(
                f"{self.API_URL}/domains/{mailbox.domain.name}/mailboxes/{mailbox.local_part}",
                json=payload,
                headers=headers,
                verify=True,
                timeout=self.API_TIMEOUT,
                allow_redirects=True,
            )
        except requests.exceptions.ConnectionError as error:
            logger.error(
                "Connection error while trying to reach %s.",
                self.API_URL,
                exc_info=error,
            )
            raise error

        if response.status_code == status.HTTP_201_CREATED:
            logger.info(
                "Mailbox successfully created on domain %s by user %s",
                str(mailbox.domain),
                request_user,
            )
            return response

        if response.status_code == status.HTTP_403_FORBIDDEN:
            logger.error(
                "[DIMAIL] 403 Forbidden: you cannot access domain %s",
                str(mailbox.domain),
            )
            raise exceptions.PermissionDenied(
                "Permission denied. Please check your MAIL_PROVISIONING_API_CREDENTIALS."
            )

        # Dimail doesn't return a clear error for duplicate yet
        # but a 500 internal error
        if response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR:
            try:
                address = session.get(
                    f"{self.API_URL}/domains/{mailbox.domain.name}/address/{mailbox.local_part}/",
                    json=payload,
                    headers=headers,
                    verify=True,
                    timeout=self.API_TIMEOUT,
                    allow_redirects=True,
                )
            except requests.exceptions.ConnectionError as error:
                logger.error(
                    "Connection error while trying to reach %s.",
                    self.API_URL,
                    exc_info=error,
                )
                raise error

            if address.status_code == status.HTTP_200_OK:
                primary = address.json()["ox_primary_email"]
                raise exceptions.ValidationError(
                    {
                        "NON_FIELD_ERRORS": [
                            f"First name + last name combination already in use in this context : {primary}."
                        ]
                    }
                )

        return self._raise_exception_for_unexpected_response(response)

    def create_user(self, user_id):
        """Send a request to dimail, to create a new user there. In dimail, user ids are subs."""

        payload = {"name": user_id, "password": "no", "is_admin": "false", "perms": []}

        try:
            response = session.post(
                f"{self.API_URL}/users/",
                headers={"Authorization": f"Basic {self.API_CREDENTIALS}"},
                json=payload,
                verify=True,
                timeout=self.API_TIMEOUT,
                allow_redirects=True,
            )
        except requests.exceptions.ConnectionError as error:
            logger.error(
                "Connection error while trying to reach %s.",
                self.API_URL,
                exc_info=error,
            )
            raise error

        if response.status_code == status.HTTP_201_CREATED:
            logger.info(
                '[DIMAIL] User "%s" successfully created on dimail',
                user_id,
            )
            return response

        if response.status_code == status.HTTP_409_CONFLICT:
            logger.info(
                '[DIMAIL] Attempt to create user "%s" which already exists.',
                user_id,
            )
            return response

        return self._raise_exception_for_unexpected_response(response)

    def create_allow(self, user_id, domain_name):
        """Send a request to dimail for a new 'allow' between user and the domain."""

        payload = {
            "user": user_id,
            "domain": domain_name,
        }

        try:
            response = session.post(
                f"{self.API_URL}/allows/",
                headers={"Authorization": f"Basic {self.API_CREDENTIALS}"},
                json=payload,
                verify=True,
                timeout=self.API_TIMEOUT,
                allow_redirects=True,
            )
        except requests.exceptions.ConnectionError as error:
            logger.error(
                "Connection error while trying to reach %s.",
                self.API_URL,
                exc_info=error,
            )
            raise error

        if response.status_code == status.HTTP_201_CREATED:
            logger.info(
                '[DIMAIL] Permissions granted for user "%s" on domain %s.',
                user_id,
                domain_name,
            )
            return response

        if response.status_code == status.HTTP_409_CONFLICT:
            logger.info(
                '[DIMAIL] Attempt to create already existing permission between "%s" and "%s".',
                user_id,
                domain_name,
            )
            return response

        return self._raise_exception_for_unexpected_response(response)

    def _raise_exception_for_unexpected_response(self, response):
        """Raise error when encountering an unexpected error in dimail."""
        try:
            error_content = json.loads(
                response.content.decode(response.encoding).replace("'", '"')
            )
        except json.decoder.JSONDecodeError:
            error_content = response.content.decode(response.encoding)

        raise requests.exceptions.HTTPError(
            f"[DIMAIL] unexpected error: {response.status_code} {error_content}"
        )

    def notify_mailbox_creation(self, recipient, mailbox_data, issuer=None):
        """
        Send email to confirm mailbox creation, provide mailbox information
        and connexion url.
        """

        title = _("Your new mailbox information")
        template_name = "new_mailbox"

        if settings.SEND_MAILBOX_PASSWORD and "password" in mailbox_data:
            template_name = "new_mailbox_password"

        self._send_mailbox_related_email(
            title, template_name, recipient, mailbox_data, issuer
        )

    def _notify_mailbox_password_reset(self, recipient, mailbox_data, issuer=None):
        """
        Send email to notify of password reset and send new password.
        """
        title = _("Your password has been updated")
        template_name = "reset_password"
        self._send_mailbox_related_email(
            title, template_name, recipient, mailbox_data, issuer
        )

    def send_login_link(self, mailbox, issuer=None):
        """
        Send magic link to secondary email.
        """
        title = _("Here is your login link")
        template_name = "send_login_link"
        recipient = mailbox.secondary_email

        login_link = f"{settings.WEBMAIL_URL}/code/{self.get_login_code(mailbox)}"
        self._send_mailbox_related_email(
            title,
            template_name,
            recipient,
            {
                "email": str(mailbox),
                "link": login_link,
            },
            issuer,
        )
        logger.info(
            "[DIMAIL] Login link for mailbox %s sent to %s.", mailbox, recipient
        )

    # pylint: disable=too-many-arguments
    # pylint: disable=too-many-positional-arguments
    def _send_mailbox_related_email(
        self, title, template_name, recipient, mailbox_data, issuer=None
    ):
        """
        Send email with new mailbox or password reset information.
        """
        context = {
            "title": title,
            "site": Site.objects.get_current(),
            "webmail_url": settings.WEBMAIL_URL,
            "mailbox_data": mailbox_data,
        }

        try:
            with override(issuer.language if issuer else settings.LANGUAGE_CODE):
                mail.send_mail(
                    context["title"],
                    render_to_string(f"mail/text/{template_name}.txt", context),
                    settings.EMAIL_FROM,
                    [recipient],
                    html_message=render_to_string(
                        f"mail/html/{template_name}.html", context
                    ),
                    fail_silently=False,
                )
        except smtplib.SMTPException as exception:
            logger.error(
                "Failed to send mailbox information to %s was not sent: %s",
                recipient,
                exception,
            )
        else:
            logger.info(
                "Information for mailbox %s sent to %s.",
                mailbox_data["email"],
                recipient,
            )

    def get_login_code(self, mailbox):
        """Get a login code from dimail."""
        if not mailbox.secondary_email or mailbox.secondary_email == str(mailbox):
            raise exceptions.ValidationError(
                "Logging in with a link requires a secondary email address. Please add a valid secondary email before trying again."
            )

        try:
            response = session.post(
                f"{self.API_URL}/domains/{mailbox.domain.name}/mailboxes/{mailbox.local_part}/code",
                json={"expires_in": 3600, "maxuse": 3},
                headers=self._get_headers(),
                verify=True,
                timeout=self.API_TIMEOUT,
                allow_redirects=True,
            )
        except requests.exceptions.ConnectionError as error:
            logger.exception(
                "Connection error while trying to reach %s.",
                self.API_URL,
                exc_info=error,
            )
            raise error

        response.raise_for_status()
        return response.json()["code"]

    def import_mailboxes(self, domain):
        """Import mailboxes from dimail - open xchange in our database.
        This is useful in case of acquisition of a pre-existing mail domain.
        Mailboxes created here are not new mailboxes and will not trigger mail notification."""

        try:
            response = session.get(
                f"{self.API_URL}/v2/domains/{domain.name}/mailboxes/",
                headers=self._get_headers(),
                verify=True,
                timeout=self.API_TIMEOUT,
                allow_redirects=True,
            )
        except requests.exceptions.ConnectionError as error:
            logger.error(
                "Connection error while trying to reach %s.",
                self.API_URL,
                exc_info=error,
            )
            raise error

        if response.status_code != status.HTTP_200_OK:
            return self._raise_exception_for_unexpected_response(response)

        dimail_mailboxes = response.json()
        people_mailboxes = models.Mailbox.objects.filter(domain=domain)

        imported_mailboxes = []
        for dimail_mailbox in dimail_mailboxes:
            try:
                address = Address(addr_spec=dimail_mailbox["email"])
            except (HeaderParseError, NonASCIILocalPartDefect) as error:
                logger.warning(
                    "Import of email %s failed with error %s",
                    dimail_mailbox["email"],
                    error,
                )
                continue

            # Skipping inactive and functional mailboxes for now
            if "ox" not in dimail_mailbox["features"]:
                logger.warning(
                    "Skipping functional mailbox: '%s'", dimail_mailbox["email"]
                )
                continue

            if address.username == "oxadmin":
                logger.warning(
                    "Not importing OX technical address: %s", dimail_mailbox["email"]
                )
                continue

            if str(address) not in [
                str(people_mailbox) for people_mailbox in people_mailboxes
            ]:
                # sometimes dimail api returns email from another domain,
                # so we decide to exclude this kind of email
                if address.domain == domain.name:
                    # creates a mailbox on our end
                    mailbox = models.Mailbox.objects.create(
                        first_name=dimail_mailbox["extras"]["ox"]["given_name"],
                        last_name=dimail_mailbox["extras"]["ox"]["sur_name"],
                        local_part=address.username,
                        domain=domain,
                        status=enums.MailboxStatusChoices.ENABLED,
                        password=make_password(None),  # unusable password
                    )
                    imported_mailboxes.append(str(mailbox))
                else:
                    logger.warning(
                        "Import of email %s failed because of a wrong domain",
                        dimail_mailbox["email"],
                    )
        return imported_mailboxes

    def disable_mailbox(self, mailbox, request_user=None):
        """Send a request to disable a mailbox to dimail API"""
        response = session.patch(
            f"{self.API_URL}/domains/{mailbox.domain.name}/mailboxes/{mailbox.local_part}",
            json={"active": "no"},
            headers=self._get_headers(),
            verify=True,
            timeout=self.API_TIMEOUT,
            allow_redirects=True,
        )
        if response.status_code == status.HTTP_200_OK:
            logger.info(
                "Mailbox %s successfully desactivated on domain %s by user %s",
                str(mailbox),
                str(mailbox.domain),
                request_user,
            )
            return response
        return self._raise_exception_for_unexpected_response(response)

    def enable_mailbox(self, mailbox, request_user=None):
        """Send a request to enable a mailbox to dimail API"""
        response = session.patch(
            f"{self.API_URL}/domains/{mailbox.domain.name}/mailboxes/{mailbox.local_part}",
            json={
                "active": "yes",
                "givenName": mailbox.first_name,
                "surName": mailbox.last_name,
                "displayName": f"{mailbox.first_name} {mailbox.last_name}",
            },
            headers=self._get_headers(),
            verify=True,
            timeout=self.API_TIMEOUT,
            allow_redirects=True,
        )
        if response.status_code == status.HTTP_200_OK:
            logger.info(
                "Mailbox %s successfully enabled on domain %s by user %s",
                str(mailbox),
                str(mailbox.domain),
                request_user,
            )
            return response
        return self._raise_exception_for_unexpected_response(response)

    def send_pending_mailboxes(self, domain):
        """Send requests for all pending mailboxes of a domain. Returns a list of failed mailboxes for this domain."""
        failed_mailboxes = []

        for mailbox in domain.mailboxes.filter(
            status=enums.MailboxStatusChoices.PENDING
        ):
            try:
                response = self.create_mailbox(mailbox)
            except requests.exceptions.HTTPError:
                failed_mailboxes.append(str(mailbox))
            else:
                mailbox.status = enums.MailDomainStatusChoices.ENABLED
                mailbox.save()

                if mailbox.secondary_email and mailbox.secondary_email != str(mailbox):
                    # send confirmation email
                    self.notify_mailbox_creation(
                        recipient=mailbox.secondary_email,
                        mailbox_data=response.json(),
                    )
                else:
                    logger.warning(
                        "Email notification for %s creation not sent "
                        "because no valid secondary email found",
                        mailbox,
                    )
        return {"failed_mailboxes": failed_mailboxes}

    def check_domain(self, domain):
        """Send a request to dimail to check domain configuration."""

        try:
            response = session.get(
                f"{self.API_URL}/domains/{domain.name}/check",
                headers={"Authorization": f"Basic {self.API_CREDENTIALS}"},
                verify=True,
                timeout=self.API_TIMEOUT,
                allow_redirects=True,
            )
        except requests.exceptions.ConnectionError as error:
            logger.error(
                "Connection error while trying to reach %s.",
                self.API_URL,
                exc_info=error,
            )
            raise error

        if response.status_code == status.HTTP_200_OK:
            return response.json()
        return self._raise_exception_for_unexpected_response(response)

    def fix_domain(self, domain):
        """Send a request to dimail to fix a domain.
        Allow to fix internal checks."""
        response = session.get(
            f"{self.API_URL}/domains/{domain.name}/fix/",
            headers={"Authorization": f"Basic {self.API_CREDENTIALS}"},
            verify=True,
            timeout=self.API_TIMEOUT,
            allow_redirects=True,
        )
        if response.status_code == status.HTTP_200_OK:
            logger.info(
                "Domain %s successfully fixed by dimail",
                str(domain),
            )
            return response.json()
        return self._raise_exception_for_unexpected_response(response)

    def fetch_domain_status(self, domain):
        """Send a request to check and update status of a domain."""
        dimail_response = self.check_domain(domain)
        if dimail_response:
            dimail_state = dimail_response["state"]
            # if domain is not enabled and dimail returns ok status, enable it
            if (
                domain.status != enums.MailDomainStatusChoices.ENABLED
                and dimail_state == "ok"
            ):
                self.send_pending_mailboxes(domain)
                domain.status = enums.MailDomainStatusChoices.ENABLED
                domain.last_check_details = dimail_response
                domain.save()
            # if dimail returns broken status, we need to extract external and internal checks
            # and manage the case where the domain has to be fixed by support
            elif dimail_state == "broken":
                external_checks = self._get_dimail_checks(
                    dimail_response, internal=False
                )
                internal_checks = self._get_dimail_checks(
                    dimail_response, internal=True
                )
                # manage the case where the domain has to be fixed by support
                if not all(external_checks.values()):
                    domain.status = enums.MailDomainStatusChoices.ACTION_REQUIRED
                    domain.last_check_details = dimail_response
                    domain.save()
                # if all external checks are ok but not internal checks, we need to fix
                # internal checks
                elif all(external_checks.values()) and not all(
                    internal_checks.values()
                ):
                    # a call to fix endpoint is needed because all external checks are ok
                    dimail_response = self.fix_domain(domain)
                    # we need to check again if all internal and external checks are ok
                    external_checks = self._get_dimail_checks(
                        dimail_response, internal=False
                    )
                    internal_checks = self._get_dimail_checks(
                        dimail_response, internal=True
                    )
                    if all(external_checks.values()) and all(internal_checks.values()):
                        domain.status = enums.MailDomainStatusChoices.ENABLED
                        domain.last_check_details = dimail_response
                        domain.save()
                    elif all(external_checks.values()) and not all(
                        internal_checks.values()
                    ):
                        domain.status = enums.MailDomainStatusChoices.FAILED
                        domain.last_check_details = dimail_response
                        domain.save()

            # if no health check data is stored on the domain, we store it now
            if not domain.last_check_details:
                domain.last_check_details = dimail_response
                domain.save()

        return dimail_response

    def _get_dimail_checks(self, dimail_response, internal: bool):
        checks = {
            key: value
            for key, value in dimail_response.items()
            if isinstance(value, dict) and value.get("internal") is internal
        }
        return {key: value.get("ok", False) for key, value in checks.items()}

    def fetch_domain_expected_config(self, domain):
        """Send a request to dimail to get domain specification for DNS configuration."""
        try:
            response = session.get(
                f"{self.API_URL}/domains/{domain.name}/spec/",
                headers={"Authorization": f"Basic {self.API_CREDENTIALS}"},
                verify=True,
                timeout=self.API_TIMEOUT,
                allow_redirects=True,
            )
        except requests.exceptions.ConnectionError as error:
            logger.exception(
                "Connection error while trying to reach %s.",
                self.API_URL,
                exc_info=error,
            )
            return []
        if response.status_code == status.HTTP_200_OK:
            # format the response to log an error if api response changed
            try:
                dimail_expected_config = [
                    {
                        "target": item["target"],
                        "type": item["type"],
                        "value": item["value"],
                    }
                    for item in response.json()
                ]
                domain.expected_config = dimail_expected_config
                domain.save()
                return dimail_expected_config
            except KeyError as error:
                logger.exception(
                    "[DIMAIL] spec expected response format changed: %s",
                    error,
                )
                return []
        else:
            logger.exception(
                "[DIMAIL] unexpected error : %s %s",
                response.status_code,
                response.content,
                exc_info=False,
            )
            return []

    def reset_password(self, mailbox):
        """Send a request to reset mailbox password."""
        if not mailbox.secondary_email or mailbox.secondary_email == str(mailbox):
            raise exceptions.ValidationError(
                _("Please add a secondary email before resetting the password.")
            )

        try:
            response = session.post(
                f"{self.API_URL}/domains/{mailbox.domain.name}/mailboxes/{mailbox.local_part}/reset_password/",
                headers=self._get_headers(),
                verify=True,
                timeout=self.API_TIMEOUT,
                allow_redirects=True,
            )
        except requests.exceptions.ConnectionError as error:
            logger.exception(
                "Connection error while trying to reach %s.",
                self.API_URL,
                exc_info=error,
            )
            raise error

        if response.status_code == status.HTTP_200_OK:
            mailbox.password = response.json()["password"]
            # send new password to secondary email
            self._notify_mailbox_password_reset(
                recipient=mailbox.secondary_email,
                mailbox_data={
                    "email": response.json()["email"],
                    "password": response.json()["password"],
                },
            )
            logger.info(
                "[DIMAIL] Password reset on mailbox %s.",
                mailbox,
            )
            return response
        return self._raise_exception_for_unexpected_response(response)

    def create_alias(self, alias, request_user=None):
        """Send a Create alias request to mail provisioning API."""

        payload = {
            "user_name": alias.local_part,
            "destination": alias.destination,
        }
        headers = self._get_headers()

        try:
            response = session.post(
                f"{self.API_URL}/domains/{alias.domain.name}/aliases/",
                json=payload,
                headers=headers,
                verify=True,
                timeout=self.API_TIMEOUT,
                allow_redirects=True,
            )
        except requests.exceptions.ConnectionError as error:
            logger.error(
                "Connection error while trying to reach %s.",
                self.API_URL,
                exc_info=error,
            )
            raise error

        if response.status_code == status.HTTP_201_CREATED:
            logger.info(
                "User %s linked alias %s to a new email.",
                request_user,
                f"{alias.local_part}@{alias.domain}",
            )
            return response

        if response.status_code == status.HTTP_403_FORBIDDEN:
            logger.error(
                "[DIMAIL] 403 Forbidden: you cannot access domain %s",
                str(alias.domain),
            )
            raise exceptions.PermissionDenied(
                "Permission denied. Please check your MAIL_PROVISIONING_API_CREDENTIALS."
            )

        if response.status_code == status.HTTP_409_CONFLICT:
            logger.error(
                "[DIMAIL] Out of sync with dimail. Admin, please import aliases for domain %s",
                str(alias.domain),
            )
            raise exceptions.ValidationError(
                {
                    "NON_FIELD_ERRORS": [
                        "Alias already exists. Your domain is out of sync, please contact our support."
                    ]
                }
            )

        return self._raise_exception_for_unexpected_response(response)

    def delete_alias(self, alias, request_user=None):
        """Send a Delete alias request to mail provisioning API."""

        headers = self._get_headers()

        try:
            response = session.delete(
                f"{self.API_URL}/domains/{alias.domain.name}/aliases/{alias.local_part}/{alias.destination}",
                json={},
                headers=headers,
                verify=True,
                timeout=self.API_TIMEOUT,
                allow_redirects=True,
            )
        except requests.exceptions.ConnectionError as error:
            logger.error(
                "Connection error while trying to reach %s.",
                self.API_URL,
                exc_info=error,
            )
            raise error

        if response.status_code == status.HTTP_204_NO_CONTENT:
            logger.info(
                "User %s removed destination %s from alias %s.",
                request_user,
                alias.destination,
                f"{alias.local_part}@{alias.domain}",
            )
            return response

        if response.status_code == status.HTTP_403_FORBIDDEN:
            logger.error(
                "[DIMAIL] 403 Forbidden: you cannot access domain %s",
                str(alias.domain),
            )
            raise exceptions.PermissionDenied(
                "Permission denied. Please check your MAIL_PROVISIONING_API_CREDENTIALS."
            )

        if response.status_code == status.HTTP_404_NOT_FOUND:
            logger.error(
                "[DIMAIL] 404, alias %s not found. Domain out of sync with dimail. Admin, please import aliases for domain %s",
                str(alias.local_part),
                str(alias.domain),
            )
            # we don't raise error because we actually want this alias to be deleted
            return response

        return self._raise_exception_for_unexpected_response(response)

    def delete_multiple_alias(self, local_part, domain_name):
        """Send a Delete alias request to mail provisioning API."""

        try:
            response = session.delete(
                f"{self.API_URL}/domains/{domain_name}/aliases/{local_part}/all",
                json={},
                headers=self._get_headers(),
                verify=True,
                timeout=self.API_TIMEOUT,
                allow_redirects=True,
            )
        except requests.exceptions.ConnectionError as error:
            logger.error(
                "Connection error while trying to reach %s.",
                self.API_URL,
                exc_info=error,
            )
            raise error
        # response.raise_for_status()

        return response

    def import_aliases(self, domain):
        """Import aliases from dimail. Useful if people fall out of sync with dimail."""

        try:
            response = session.get(
                f"{self.API_URL}/domains/{domain.name}/aliases/",
                headers=self._get_headers(),
                verify=True,
                timeout=self.API_TIMEOUT,
                allow_redirects=True,
            )
        except requests.exceptions.ConnectionError as error:
            logger.error(
                "Connection error while trying to reach %s.",
                self.API_URL,
                exc_info=error,
            )
            raise error

        if response.status_code != status.HTTP_200_OK:
            return self._raise_exception_for_unexpected_response(response)

        incoming_aliases = response.json()
        known_aliases = [
            (known_alias.local_part, known_alias.destination)
            for known_alias in models.Alias.objects.filter(domain=domain)
        ]

        imported_aliases = []
        for incoming_alias in incoming_aliases:
            if (
                incoming_alias["username"],
                incoming_alias["destination"],
            ) not in known_aliases:
                try:
                    new_alias = models.Alias.objects.create(
                        local_part=incoming_alias["username"],
                        destination=incoming_alias["destination"],
                        domain=domain,
                    )
                except (HeaderParseError, NonASCIILocalPartDefect) as err:
                    logger.warning(
                        "Import of alias %s to %s failed with error %s",
                        incoming_alias["username"],
                        incoming_alias["destination"],
                        err,
                    )
                else:
                    imported_aliases.append(str(new_alias))
        return imported_aliases
