# -*- coding: utf-8 -*-
# Copyright (c) 2013 Tomasz Wójcik <tomek@bthlabs.pl>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
"""
lib.testing
===========

Various utilities used in testing.
"""

import os
import smtplib
import tempfile

HTML_BODY = u"""<html>
<head></head>
<body><p>I'm a helicopter!</p></body>
</html>"""

TEXT_BODY = u"""I'm a helicopter!"""


class MockSMTP(object):
    """A class that mocks ``smtp.SMTP``."""

    debuglevel = 0
    file = None
    helo_resp = None
    ehlo_msg = "ehlo"
    ehlo_resp = None
    does_esmtp = 0
    default_port = smtplib.SMTP_PORT

    def __init__(self, host='', port=0, local_hostname=None, timeout=0):
        self._host = host
        self._port = port
        self._local_hostname = local_hostname
        self._timeout = timeout
        self._call_stack = {}

    def __append_call(self, method, args, kwargs):
        if method not in self._call_stack:
            self._call_stack[method] = []

        self._call_stack[method].append((args, kwargs))

    def set_debuglevel(self, debuglevel):
        self.debuglevel = debuglevel

    def _get_socket(self, port, host, timeout):
        return None

    def connect(self, host='localhost', port=0):
        self.__append_call('connect', [], dict(host=host, port=port))

    def send(self, str):
        self.__append_call('connect', [str], {})

    def putcmd(self, cmd, args=""):
        self.__append_call('connect', [cmd], dict(args=args))

    def getreply(self):
        self.__append_call('getreply', [], dict())

    def docmd(self, cmd, args=""):
        self.__append_call('docmd', [cmd], dict(args=args))

    def helo(self, name=''):
        self.__append_call('helo', [], dict(name=name))

    def ehlo(self, name=''):
        self.__append_call('ehlo', [], dict(name=name))

    def has_extn(self, opt):
        self.__append_call('has_extn', [opt], dict())

    def help(self, args=''):
        self.__append_call('help', [], dict(args=args))

    def rset(self):
        self.__append_call('rset', [], dict())

    def noop(self):
        self.__append_call('noop', [], dict())

    def mail(self, sender, options=[]):
        self.__append_call('mail', [sender], dict(options=options))

    def rcpt(self, recip, options=[]):
        self.__append_call('rcpt', [recip], dict(options=options))

    def data(self, msg):
        self.__append_call('data', [msg], dict())

    def verify(self, address):
        self.__append_call('verify', [address], dict())

    vrfy = verify

    def expn(self, address):
        self.__append_call('expn', [address], dict())

    def ehlo_or_helo_if_needed(self):
        self.__append_call('ehlo_or_helo_if_needed', [], dict())

    def login(self, user, password):
        self.__append_call('login', [user, password], dict())

    def starttls(self, keyfile=None, certfile=None):
        self.__append_call('starttls', [],
                           dict(keyfile=keyfile,
                                certfile=certfile))

    def sendmail(self,
                 from_addr,
                 to_addrs,
                 msg,
                 mail_options=[],
                 rcpt_options=[]):
        _args = [from_addr, to_addrs, msg]
        _kwargs = dict(mail_options=mail_options, rcpt_options=rcpt_options)
        self.__append_call('sendmail', _args, _kwargs)

    def close(self):
        self.__append_call('close', [], dict())

    def quit(self):
        self.__append_call('quit', [], dict())


class MockSMTPSSL(MockSMTP):
    """A class that mocks ``smtp.SMTP_SSL``."""

    default_port = smtplib.SMTP_SSL_PORT


class BaseTestCase(object):
    """Base class for Envelopes test cases."""

    @classmethod
    def setUpClass(cls):
        cls._tempfiles = []

    @classmethod
    def tearDownClass(cls):
        for tmpfile in cls._tempfiles:
            os.unlink(tmpfile)

    def tearDown(self):
        self._unpatch_smtplib()

    def _patch_smtplib(self):
        self._orig_smtp = smtplib.SMTP
        smtplib.SMTP = MockSMTP

        self._orig_smtp_ssl = smtplib.SMTP_SSL
        smtplib.SMTP_SSL = MockSMTPSSL

    def _unpatch_smtplib(self):
        if hasattr(self, '_orig_smtp'):
            smtplib.SMTP = self._orig_smtp

        if hasattr(self, '_orig_smtp_ssl'):
            smtplib.SMTP_SSL = self._orig_smtp_ssl

    def _dummy_message(self):
        return dict({
            'to_addr': ('to@example.com', 'Example To'),
            'from_addr': ('from@example.com', 'Example From'),
            'subject': "I'm a helicopter!",
            'html_body': HTML_BODY,
            'text_body': TEXT_BODY,
            'cc_addr': [
                'cc1@example.com', 'Example CC2 <cc2@example.com>',
                ('cc3@example.com', 'Example CC3')
            ],
            'bcc_addr': [
                'bcc1@example.com', 'Example BCC2 <bcc2@example.com>',
                ('bcc3@example.com', 'Example BCC3')
            ],
            'reply_to_addr': [
                'replyto1@example.com', 'Reply To2 <replyto2@example.com>',
                ('replyto3@example.com', 'Reply To3')
            ],
            'headers': {
                'X-Mailer': 'Envelopes by BTHLabs'
            },
            'charset': 'utf-8'
        })

    def _tempfile(self, **kwargs):
        fd, path = tempfile.mkstemp(**kwargs)
        os.close(fd)
        self._tempfiles.append(path)

        return path
