# encoding: utf-8

__all__ = [ 'SMTPResponse', 'SMTPClientWithResponse', 'SMTPClientWithResponse_SSL' ]

from smtplib import _have_ssl, SMTP
import smtplib

import logging

logger = logging.getLogger(__name__)

class SMTPResponse(object):

    def __init__(self, host=None, port=None, ssl=None, exception=None):
        self.host = host
        self.port = port
        self.ssl = ssl
        self.responses = []
        self.exception = exception
        #self.complete = False
        self.success = None
        self.from_addr = None
        self.esmtp_opts = None
        self.rcpt_options = None
        self.to_addr = None
        self.status_code = None
        self.status_text = None
        self.last_command = None

    def set_status(self, command, code, text):
        self.responses.append( [command, code, text] )
        self.status_code = code
        self.status_text = text
        self.last_command = command

    def set_exception(self, exc):
        self.exception = exc

    def raise_if_needed():
        if self.exception:
            raise self.exception

    @property
    def error(self):
        return self.exception

    def __repr__(self):
        return "<emails.smtp.SMTPResponse status_code=%s status_text=%s>" % (self.status_code.__repr__(),
                                                                              self.status_text.__repr__())


#class SMTPCommandsLog:


class SMTPClientWithResponse(SMTP):

    def __init__(self, parent, **kwargs):
        self.parent = parent
        self.make_response = parent.make_response
        self._last_smtp_response = (None, None)
        self.tls = kwargs.pop('tls', False)
        self.debug = kwargs.pop('debug', False)
        self.user = kwargs.pop('user', None)
        self.password = kwargs.pop('password', None)
        SMTP.__init__(self, **kwargs)
        self.initialize()

    def initialize(self):
        if self.debug:
            self.set_debuglevel(1)
        if self.tls:
            self.ehlo()
            self.starttls()
            self.ehlo()
        if self.user:
            self.login(user=self.user, password=self.password)
        self.ehlo_or_helo_if_needed()
        return self

    def quit(self):
        """Closes the connection to the email server."""
        try:
            super(self, SMTPClientWithResponse).quit()
        except (smtplib.SMTPServerDisconnected, ):
            # This happens when calling quit() on a TLS connection
            # sometimes, or when the connection was already disconnected
            # by the server.
            self.close()

    def data(self, msg):
        (code, msg) = SMTP.data(self, msg)
        self._last_smtp_response = (code, msg)
        return code, msg


    def _send_one_mail(self, from_addr, to_addr, msg, mail_options=[], rcpt_options=[]):

        esmtp_opts = []
        if self.does_esmtp:
            if self.has_extn('size'):
                esmtp_opts.append("size=%d" % len(msg))
            for option in mail_options:
                esmtp_opts.append(option)

        response = self.make_response()

        response.from_addr = from_addr
        response.esmtp_opts = esmtp_opts[:]

        (code, resp) = self.mail(from_addr, esmtp_opts)
        response.set_status('mail', code, resp)

        if code != 250:
            self.rset()
            exc = smtplib.SMTPSenderRefused(code, resp, from_addr)
            response.set_exception(exc)
            return response

        response.to_addr = to_addr
        response.rcpt_options = rcpt_options[:]

        (code, resp) = self.rcpt(to_addr, rcpt_options)
        response.set_status('rcpt', code, resp)

        if (code != 250) and (code != 251):
            self.rset()
            exc = smtplib.SMTPRecipientsRefused(to_addr)
            response.set_exception(exc)
            return response

        (code, resp) = self.data(msg)
        response.set_status('data', code, resp)
        if code != 250:
            self.rset()
            exc = smtplib.SMTPDataError(code, resp)
            response.set_exception(exc)
            return response

        response.success = True
        return response

    def sendmail(self, from_addr, to_addrs, msg, mail_options=[], rcpt_options=[]):

        # Send one email and returns one response
        if not to_addrs:
            raise StopIteration

        assert isinstance(to_addrs, (list, tuple))

        if len(to_addrs)>1:
            logger.warning('Beware: emails.smtp.client.SMTPClientWithResponse.sendmail sends full message to each email')

        return [ self._send_one_mail(from_addr, to_addr, msg, mail_options, rcpt_options) \
                        for to_addr in to_addrs ]



if _have_ssl:

    from smtplib import SMTP_SSL
    import ssl

    class SMTPClientWithResponse_SSL(SMTPClientWithResponse, SMTP_SSL):

        def quit(self):
            """Closes the connection to the email server."""
            try:
                super(self, SMTPClientWithResponse_SSL).quit()
            except (ssl.SSLError, smtplib.SMTPServerDisconnected):
                # This happens when calling quit() on a TLS connection
                # sometimes, or when the connection was already disconnected
                # by the server.
                self.close()

else:

    class SMTPClientWithResponse_SSL:
        def __init__(self, *args, **kwargs):
            # should raise import error here
            import ssl



