from __future__ import unicode_literals

from ..elements.elementbase import Attribute, LogicElement
from ..tags.content import ContentElementMixin
from .. import logic
from .. import namespaces
from ..render import render_object
from ..template.rendercontainer import RenderContainer
from ..mail import Email

import logging
log = logging.getLogger('moya.email')


class EmailElement(LogicElement):
    """Define an email."""
    xmlns = namespaces.email

    class Meta:
        tag_name = "email"

    class Help:
        synopsis = "define an email"

    from_ = Attribute("From email", required=False, default=None, name="from")
    to = Attribute("To email address", required=False, default=None)
    cc = Attribute("CC email address", required=False, default=None)
    bcc = Attribute("BCC email address", required=False, default=None)
    replyto = Attribute("Reply to email address", required=False, default=None)
    subject = Attribute("Email subjects", default=None)


class Text(LogicElement, ContentElementMixin):
    """Add text template to an email."""
    xmlns = namespaces.email

    class Help:
        synopsis = "add text to an email"

    template = Attribute("Template path", type="template", required=False)
    content = Attribute("Content Element", required=False, type="elementref")
    from_ = Attribute("From email", required=False, default=None, name="from")

    def logic(self, context):
        template, content = self.get_parameters(context, 'template', 'content')
        app = self.get_app(context)
        email = context['email']
        if template:
            template = app.resolve_template(template)
            render_container = RenderContainer.create(app, template=template)
            render_container.update(email.data)
            text = render_object(render_container, self.archive, context, "text")
        else:
            for defer in self.generate_content(context, content, app, td=email.data):
                yield defer
            content = context['.content']
            text = render_object(content, self.archive, context, "text")
        email.text = text


class HTML(LogicElement, ContentElementMixin):
    """Add an HTML template to an email."""
    xmlns = namespaces.email

    class Help:
        synopsis = "add HTML to an email"

    template = Attribute("Template path", type="template", required=False)
    content = Attribute("Content Element", required=False, type="elementref")
    from_ = Attribute("From email", required=False, default=None, name="from")

    def logic(self, context):
        template, content = self.get_parameters(context, 'template', 'content')
        app = self.get_app(context)
        email = context['email']
        if template:
            template = app.resolve_template(template)
            render_container = RenderContainer.create(app, template=template)
            render_container.update(email.data)
            html = render_object(render_container, self.archive, context, "html")
        else:
            for defer in self.generate_content(context, content, app, td=email.data):
                yield defer
            content = context['.content']
            html = render_object(content, self.archive, context, "html")
        email.html = html


class Get(LogicElement):
    """Get an previously defined email object."""
    xmlns = namespaces.email

    class Help:
        synopsis = "get an email object"

    dst = Attribute("Destination to store exception object", type="reference", required=True)
    email = Attribute("Destination to store exception object", type="elementref")
    subject = Attribute("Email subject", default=None)
    data = Attribute("Template / content data", type="expression", default=None)

    from_ = Attribute("From email", required=False, default=None, name="from", map_to="from")
    to = Attribute("To email address", required=False, default=None)
    cc = Attribute("CC email address", required=False, default=None)
    bcc = Attribute("BCC email address", required=False, default=None)
    replyto = Attribute("Reply to email address", required=False, default=None)

    def get_email(self, context):
        dst, data, email_ref, subject, from_ = self.get_parameters(context,
                                                                   'dst',
                                                                   'data',
                                                                   'email',
                                                                   'subject',
                                                                   'from')
        if data is None:
            data = {}
        data.update(self.get_let_map(context))

        with context.data_scope(data):
            app = self.get_app(context)
            email_app, email_element = self.get_element(email_ref, app)

            subject = email_element.subject(context) or subject
            from_ = email_element.get_parameter(context, 'from') or from_

            emails = email_element.get_parameters_map(context, 'from', 'to', 'cc', 'bcc', 'replyto')
            for k, v in self.get_parameters_map(context, 'from', 'to', 'cc', 'bcc', 'replyto').items():
                if v is not None:
                    emails[k] = v

            email = Email(data=data)
            email.app = email_app
            email.email_element = email_element
            email.subject = context.sub(subject)
            email.set_from(from_)
            email.add_to(emails['to'])
            email.add_cc(emails['cc'])
            email.add_bcc(emails['bcc'])
            email.replyto = emails['replyto']
            return email

    def logic(self, context):
        email = context[self.dst(context)] = self.get_email(context)
        email_app, email_element = self.get_element(self.email(context), self.get_app(context))
        with self.call(context, app=email_app, email=email):
            yield logic.DeferNodeContents(email_element)


class Send(Get):
    """Send an email."""

    class Help:
        synopsis = "send an email"

    xmlns = namespaces.email

    dst = Attribute("Destination to store exception object", type="reference", required=False)
    src = Attribute("Source email", type="index", default=None)
    smtp = Attribute("SMTP server", default='')
    fail_silently = Attribute("Should mail exceptions be ignored?", type="boolean", default=True)

    def logic(self, context):
        fail_silently = self.fail_silently(context)
        _email = self.src(context)
        if _email is None:
            email = self.get_email(context)
            with self.call(context, app=email.app, email=email):
                yield logic.DeferNodeContents(email.email_element)

        dst = self.dst(context)
        if dst is not None:
            context[self.dst(context)] = email

        if context.get('.debug', False):
            context['.console'].obj(context, email)

        mail_server = self.archive.get_mailserver(self.smtp(context))
        try:
            mail_server.send(email)
            log.info('sent email to "{}", subject "{}"'.format(email.to_text, email.subject or ''))
        except Exception as e:
            log.error('failed to send email to "{}", with subject "{}"'.format(email.to_text, email.subject or ''))
            if not fail_silently:
                log.error('failed to sent email to "{}", with subject "{}"'.format(email.to_text, email.subject or ''))
                self.throw('email.send-failed',
                           "Moya was unable to send email '{}' ({})".format(_email, e),
                           diagnosis="Check the [smtp:] section in settings, and that the mail server is running.",
                           info={'email': _email, 'pyerror': e})
