#  Copyright (C) 2021
#  ABM, Moscow
#
#  UNPUBLISHED PROPRIETARY MATERIAL.
#  ALL RIGHTS RESERVED.
#
#  Authors: Vasya Svintsov <v.svintsov@techokert.ru>

import logging
from dataclasses import dataclass
from typing import Union, List, Optional

from aiohttp import ClientSession
from dict_caster import DictCaster, Item
from furl import furl
from http_tools import HttpServer, Answer, WrappedAnswer
from http_tools.attached_file import AttachedFile

from http_tools.request import IncomingRequest


from .email_clients.amazon_ses_email_client.amazon_ses_email_client import AmazonSESEmailClient
from .email_clients.http_email_client.http_email_client import HTTPEmailClient
from .email_clients.smtp_email_client.smtp_email_client import SMTPEmailClient
from .sending_result import SendingResult


logger = logging.getLogger(__name__)


class EmailSender:
    @dataclass
    class _DefaultConfig:
        default_from_name: str = None

    @dataclass
    class _BaseConfig:
        default_from_email: str

    @dataclass
    class SMTPConfig(_DefaultConfig, SMTPEmailClient.Config, _BaseConfig):
        ...

    @dataclass
    class AmazonSESConfig(_DefaultConfig, AmazonSESEmailClient.Config, _BaseConfig):
        ...

    @dataclass
    class HTTPConfig(_DefaultConfig, HTTPEmailClient.Config):
        default_from_email: str = None

    @dataclass
    class Context(HTTPEmailClient.Context):
        session: ClientSession = None
        http_server: HttpServer = None

    def __init__(self,
                 config: Union[SMTPConfig, AmazonSESConfig, HTTPConfig],
                 context: Context,
                 path_prefix: Optional[str] = None,
                 allow_accept_http_requests: bool = False
                 ):
        self.config = config

        if isinstance(self.config, self.SMTPConfig):
            self._email_client = SMTPEmailClient(self.config)
        elif isinstance(self.config, self.AmazonSESConfig):
            self._email_client = AmazonSESEmailClient(self.config)
        elif isinstance(self.config, self.HTTPConfig):
            self._email_client = HTTPEmailClient(self.config, context)
        else:
            raise TypeError("InappropriateConfig")

        if context.http_server is not None and allow_accept_http_requests:
            path = furl(path_prefix).add(path=HTTPEmailClient.URL).url
            context.http_server.register_handler(path, self._send)

        logger.info(f"{type(self).__name__} init. EmailClientType: {type(self._email_client)}")

    async def send(self,
                   to_recipients: Union[str, List[str]],
                   subject: str,
                   body: str,
                   source_email_account: Optional[str] = None,
                   *,
                   source_email_account_name: Optional[str] = None,
                   attachments: Optional[List[AttachedFile]] = None,
                   cc_recipients: Optional[Union[str, List[str]]] = None,
                   bcc_recipients: Optional[Union[str, List[str]]] = None
                   ) -> SendingResult:
        """
        Send the email.

        Note: source_email_account must be verified.

        :param to_recipients: The destination email accounts.
        :param subject: The subject of the email.
        :param body: The plain text/HTML version of the body of the email.
        :param source_email_account: The source email account.
        :param source_email_account_name: The name of the source email account.
        :param attachments: The attachment part of the body of the email.
        :param cc_recipients: The list of recipients on the 'CC'.
        :param bcc_recipients: The list of recipients on the 'BCC'.

        """
        if source_email_account is None:
            source_email_account = self.config.default_from_email
        if source_email_account_name is None:
            source_email_account_name = self.config.default_from_name

        result = await self._email_client.send(
            to_recipients, subject, body, source_email_account,
            source_email_account_name=source_email_account_name,
            attachments=attachments,
            cc_recipients=cc_recipients,
            bcc_recipients=bcc_recipients
        )
        return result

    async def _send(self, request: IncomingRequest) -> Answer:
        values, attached_files = DictCaster(
            Item("values", dict),
            Item("attached_files", None, optional=True),
        ).cast_and_return(request.key_value_arguments)

        send_params = DictCaster(
            Item("subject", str),
            Item("body", str),
            Item("to_recipients", list),
            Item("source_email_account", str, optional=True),
            Item("source_email_account_name", str, optional=True),
            Item("cc_recipients", list, optional=True),
            Item("bcc_recipients", list, optional=True),
        ).cast(values)

        return WrappedAnswer(await self.send(**send_params, attachments=attached_files))
