from .version import __version__

import base64
import sys
from email.mime.base import MIMEBase

try:
    from urllib.error import HTTPError  # pragma: no cover
except ImportError: # pragma: no cover
    from urllib2 import HTTPError  # pragma: no cover

try:
    import rfc822
except ImportError:
    import email.utils as rfc822

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.core.mail import EmailMultiAlternatives
from django.core.mail.backends.base import BaseEmailBackend
from gae_tasks import tasks

import sendgrid
from sendgrid.helpers.mail import (
    Attachment,
    Category,
    Content,
    Email,
    Mail,
    Personalization,
    Substitution
)


class SendGridBackend(BaseEmailBackend):
    '''
    SendGrid Web API Backend
    '''
    def __init__(self, fail_silently=False, **kwargs):
        super(SendGridBackend, self).__init__(
            fail_silently=fail_silently, **kwargs)
        self.api_key = getattr(settings, "SENDGRID_API_KEY", None)

        if not self.api_key:
            raise ImproperlyConfigured('''
                SENDGRID_API_KEY must be declared in settings.py''')

    def send_messages(self, emails):

        if not emails:
            return

        tasks.add_task(['email'], self._send_messages, emails)

        return len(emails)

    @classmethod
    def _send_messages(cls, emails):
        for email in emails:
            tasks.add_task(['email'], cls._send_message, email)

    @classmethod
    def _send_message(cls, email):
        api_key = getattr(settings, "SENDGRID_API_KEY", None)
        sg = sendgrid.SendGridAPIClient(apikey=api_key)
        version = 'sendgrid/{0};django'.format(__version__)
        sg.client.request_headers['User-agent'] = version
        try:
            sg.client.mail.send.post(request_body=cls._build_sg_mail(email))
        except HTTPError as e:
            if not cls.fail_silently:
                raise

    def _build_sg_mail(self, email):
        mail = Mail()
        from_name, from_email = rfc822.parseaddr(email.from_email)
        # Python sendgrid client should improve
        # sendgrid/helpers/mail/mail.py:164
        if not from_name:
            from_name = None
        mail.set_from(Email(from_email, from_name))
        mail.set_subject(email.subject)

        personalization = Personalization()
        for e in email.to:
            personalization.add_to(Email(e))
        for e in email.cc:
            personalization.add_cc(Email(e))
        for e in email.bcc:
            personalization.add_bcc(Email(e))
        personalization.set_subject(email.subject)
        mail.add_content(Content("text/plain", email.body))
        if isinstance(email, EmailMultiAlternatives):
            for alt in email.alternatives:
                if alt[1] == "text/html":
                    mail.add_content(Content(alt[1], alt[0]))
        elif email.content_subtype == "html":
            mail.contents = []
            mail.add_content(Content("text/plain", ' '))
            mail.add_content(Content("text/html", email.body))

        if hasattr(email, 'categories'):
            for c in email.categories:
                mail.add_category(Category(c))

        if hasattr(email, 'template_id'):
            mail.set_template_id(email.template_id)
            if hasattr(email, 'substitutions'):
                for k, v in email.substitutions.items():
                    personalization.add_substitution(Substitution(k, v))

        for k, v in email.extra_headers.items():
            mail.add_header({k: v})

        for attachment in email.attachments:
            if isinstance(attachment, MIMEBase):
                attach = Attachment()
                attach.set_filename(attachment.get_filename())
                attach.set_content(base64.b64encode(attachment.get_payload()))
                mail.add_attachment(attach)
            elif isinstance(attachment, tuple):
                attach = Attachment()
                attach.set_filename(attachment[0])
                base64_attachment = base64.b64encode(attachment[1])
                if sys.version_info >= (3,):
                    attach.set_content(str(base64_attachment, 'utf-8'))
                else:
                    attach.set_content(base64_attachment)
                attach.set_type(attachment[2])
                mail.add_attachment(attach)

        mail.add_personalization(personalization)
        return mail.get()