# -*- coding: utf-8 -*-
import base64
import mimetypes
import requests
import os.path

from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from requests.structures import CaseInsensitiveDict

from events.common_app.utils import get_user_ip_address
from events.yauth_contrib.auth import TvmAuth


class Address:
    def __init__(self, email, name=''):
        self.email = self._get_punnycode_email(email)
        self.name = self._get_encoded_name(name)

    def _get_punnycode_email(self, email):
        parts = email.split('@')
        if len(parts) != 2:
            return email
        return '@'.join([parts[0], parts[1].encode('idna').decode()])

    def _get_encoded_name(self, name):
        if not name:
            return name
        b64_s = base64.b64encode(name.encode()).decode()
        return f'=?utf-8?B?{b64_s}?='

    def getvalue(self):
        value = {
            'email': self.email,
        }
        if self.name:
            value['name'] = self.name
        return value


class Attachment:
    default_mime_type = 'application/octet-stream'
    text_mime_type = 'text/plain;charset=utf-8'

    def __init__(self, attach_name, content, mime_type=None):
        self.attach_name = attach_name
        self.binary_content = base64.b64encode(content).decode()
        self.mime_type = mime_type or self._get_mime_type(attach_name)

    def _get_mime_type(self, attach_name):
        mime_type = None
        ext = os.path.splitext(attach_name)[1]
        if ext == '.txt':
            mime_type = self.text_mime_type
        elif ext == '.eml':
            mime_type = self.default_mime_type
        else:
            mime_type = mimetypes.types_map.get(ext, self.default_mime_type)
        return mime_type

    def getvalue(self):
        return {
            'filename': self.attach_name,
            'data': self.binary_content,
            'mime_type': self.mime_type,
        }


class SenderClient:
    default_email = 'robot-forms@yandex-team.ru'
    default_name = _('Яндекс.Формы')
    default_user_agent = 'forms'

    def __init__(self, has_ugc=True):
        self.has_ugc = has_ugc
        self.from_address = self._get_default_address()
        self.to_addresses = []
        self.attachments = []
        self.headers = CaseInsensitiveDict()

    def _get_default_address(self):
        return Address(self.default_email, self.default_name)

    def add_attachment(self, attach: Attachment):
        self.attachments.append(attach.getvalue())

    def add_address(self, address: Address):
        self.to_addresses.append(address.getvalue())

    def add_header(self, name, value):
        self.headers[name] = value

    def set_from_address(self, address: Address):
        self.from_address = address

    def set_reply_to(self, address: Address):
        if address.name:
            value = f'{address.name} <{address.email}>'
        else:
            value = address.email
        self.headers['Reply-To'] = value

    def send_email(self, campaign, args):
        sender_data = {
            'async': True,
            'args': args,
            'to': self.to_addresses,
            'from_email': self.from_address.email,
            'from_name': self.from_address.name,
            'ignore_empty_email': True,
            'has_ugc': self.has_ugc,
        }
        if self.attachments:
            sender_data['attachments'] = self.attachments

        self.headers['User-Agent'] = self.default_user_agent
        self.headers['X-Sender-Real-User-IP'] = get_user_ip_address()

        sender_url = settings.SENDER_URL.format(
            account=settings.SENDER_ACCOUNT,
            campaign=campaign,
        )
        return requests.post(
            sender_url,
            json=sender_data,
            auth=TvmAuth(settings.SENDER_TVM2_CLIENT),
            headers=dict(self.headers.items()),
            verify=settings.YANDEX_ROOT_CERTIFICATE,
            timeout=settings.DEFAULT_TIMEOUT,
        )


def send_email(campaign, args, emails, has_ugc=True):
    sender = SenderClient(has_ugc)
    for email in emails:
        sender.add_address(Address(email))
    sender.send_email(campaign, args)
