import SilvaMailingTestCase
from DateTime import DateTime
from Products.Silva.Versioning import VersioningError
from Products.SilvaMailing.content import html_to_string

import unittest
import os
import cgi
import re
import time

here = os.path.abspath(os.path.dirname(__file__))

class MailingFolderTest(SilvaMailingTestCase.SilvaMailingTestCase):
    def afterSetUp(self):
        super(MailingFolderTest, self).afterSetUp()
        self.folder = self.addObject(self.getRoot(), 'MailingFolder',
                                        'folder',
                                        title='Mailing Folder',
                                        product='SilvaMailing')
        self.folder.max_verification_time_days = 7
        self.testdoc = self.addObject(self.folder, 'MailingDocument',
                                      'testdoc', title='Test Document',
                                      product='SilvaMailing')
        fp = open(os.path.join(here, 'test.jpg'))
        try:
            self.folder.manage_addProduct['Silva'].manage_addImage(
                'someimage', 'Some Image', fp)
        finally:
            fp.close()
        self.someimage = self.folder.someimage
        self.testdoc.get_editable().content.manage_edit(
            '<doc><image target="_self" title="Some Image" link_to_hires="0" '
            'link="" path="someimage" alignment=""/></doc>')
        self.mailhost = self.folder.service_subscriptions_mailhost = \
            FakeMaildropHost()

    def test_subscribe(self):
        subscriptions = self.folder.subscriptions()
        self.assertEquals(0, len(subscriptions))

        self.folder.subscribe('foo@bar.com')
        subscriptions = self.folder.subscriptions()
        self.assertEquals(1, len(subscriptions))
        self.assertEquals('foo@bar.com', subscriptions[0])

        self.folder.subscribe('bar@baz.com')
        subscriptions = self.folder.subscriptions()
        self.assertEquals(sorted(['foo@bar.com', 'bar@baz.com']),
                          sorted(subscriptions))

        # no duplicates
        self.assertRaises(ValueError, self.folder.subscribe, 'foo@bar.com')

        # no removing non-existent addresses
        self.assertRaises(ValueError, self.folder.unsubscribe,
                          'baz@qux.com')

    def test_email_send_no_viewable(self):
        mh = self.mailhost

        self.assertEquals(0, len(mh.sent))

        self.assertRaises(VersioningError, self.folder.send_as_mailing,
                          '/'.join(self.testdoc.getPhysicalPath()),
                          test_only=True,
                          test_email='foo@bar.com')

        self.assertEquals(0, len(mh.sent))

    def test_email_send_viewable(self):
        mh = self.mailhost
        self.testdoc.set_unapproved_version_publication_datetime(DateTime())
        self.testdoc.approve_version()
        self.assertEquals(0, len(mh.sent))
        self.folder.send_as_mailing(
            '/'.join(self.testdoc.getPhysicalPath()),
            test_only=True,
            test_email='foo@bar.com')
        self.assertEquals(1, len(mh.sent))

    def test_email_send_content(self):
        mh = self.mailhost
        self.testdoc.get_editable().content.manage_edit(
            '<doc><p type="normal">foo</p></doc>')
        self.testdoc.set_unapproved_version_publication_datetime(DateTime())
        self.testdoc.approve_version()

        self.assertEquals(0, len(mh.sent))
        self.folder.send_as_mailing(
            '/'.join(self.testdoc.getPhysicalPath()),
            test_only=True,
            test_email='foo@bar.com')
        self.assertEquals(1, len(mh.sent))
        self.assert_('<p class="p">foo</p>' in mh.sent[0][2])

    def test_email_mime_multipart(self):
        mh = self.mailhost
        self.testdoc.set_unapproved_version_publication_datetime(DateTime())
        self.testdoc.approve_version()

        self.assertEquals(0, len(mh.sent))
        self.folder.send_as_mailing(
            '/'.join(self.testdoc.getPhysicalPath()),
            test_only=True,
            test_email='foo@bar.com')

        body = mh.sent[0][2]
        self.assert_('Content-Type: multipart/alternative' in body)

    def test_email_images_absolute_url(self):
        mh = self.mailhost
        self.testdoc.set_unapproved_version_publication_datetime(DateTime())
        self.testdoc.approve_version()

        self.assertEquals(0, len(mh.sent))
        self.folder.send_as_mailing(
            '/'.join(self.testdoc.getPhysicalPath()),
            test_only=True,
            test_email='foo@bar.com')
        body = mh.sent[0][2]
        # XXX hope we can assume the host name doesn't change in future
        # ZopeTestCase versions...
        self.assert_('src="http://nohost/root/folder/someimage"' in body)

    def test_subscription_stored_in_metadata(self):
        self.folder.subscribe('foo@bar.com')
        subscriptions = self.folder.subscriptions()
        self.assertEquals(1, len(subscriptions))
        subscriptions = self.root.service_metadata.getMetadataValue(
            self.folder, 'silva-mailing-subscriptions', 'subscriptions')
        subscriptions = [s.strip() for s in subscriptions.split('\n')
                         if s.strip()]
        self.assertEquals(1, len(subscriptions))

    def test_pending_subscriptions_cleanup(self):
        self.folder.start_subscription('foo@bar.com', True)
        self.assert_('foo@bar.com' in self.folder._pending)

        self.folder._cleanup_pending()
        self.assert_('foo@bar.com' in self.folder._pending)

        self.folder.max_verification_time_days = 0
        self.folder._cleanup_pending()
        self.assert_('foo@bar.com' not in self.folder._pending)

    verreg = re.compile('verification=([0-9a-f]+)')
    def test_pending_subscriptions_email(self):
        mailhost = self.mailhost
        self.assertEquals(0, len(mailhost.sent))

        self.folder.start_subscription('foo@bar.com', True)
        self.assertEquals(1, len(mailhost.sent))
        self.assert_(self.folder.absolute_url() in mailhost.sent[0][2])

        vernum = self.verreg.search(mailhost.sent[0][2]).group(1)
        self.assertEquals(vernum, self.folder._pending['foo@bar.com'][0])

    def test_subscription_subscribe_fully(self):
        mailhost = self.mailhost
        self.assertEquals(0, len(mailhost.sent))
        self.assertEquals([], self.folder.subscriptions())

        self.folder.start_subscription('foo@bar.com', True)
        self.assertEquals(1, len(mailhost.sent))
        vernum = self.verreg.search(mailhost.sent[0][2]).group(1)

        self.folder.finish_subscription('foo@bar.com', vernum)
        self.assertEquals(['foo@bar.com'], self.folder.subscriptions())

    def test_subscription_unsubscribe_fully(self):
        mailhost = self.mailhost
        self.assertEquals(0, len(mailhost.sent))
        self.assertEquals([], self.folder.subscriptions())

        self.folder.subscribe('foo@bar.com')
        self.assertEquals(['foo@bar.com'], self.folder.subscriptions())
        self.folder.start_subscription('foo@bar.com', False)
        vernum = self.verreg.search(self.mailhost.sent[0][2]).group(1)
        self.assert_('foo@bar.com' in self.folder._pending)

        self.folder.finish_subscription('foo@bar.com', vernum)
        self.assertEquals([], self.folder.subscriptions())

    def test_subscription_double_request(self):
        mailhost = self.mailhost
        self.assertEquals(0, len(mailhost.sent))

        self.assertEquals({}, self.folder._pending)
        self.folder.start_subscription('foo@bar.com', True)
        self.assertEquals(['foo@bar.com'], self.folder._pending.keys())
        value = self.folder._pending['foo@bar.com']
        self.assertEquals(1, len(mailhost.sent))
        time.sleep(1)

        # mail should be re-sent, timestamp refreshed, but not more keys in
        # pending dict
        self.folder.start_subscription('foo@bar.com', True)
        self.assertEquals(['foo@bar.com'], self.folder._pending.keys())
        self.assertEquals(value[0], self.folder._pending['foo@bar.com'][0])
        self.assertEquals(value[2], self.folder._pending['foo@bar.com'][2])
        self.assert_(value[1] < self.folder._pending['foo@bar.com'][1])
        self.assertEquals(2, len(mailhost.sent))

    def test_subscription_cancel_pending(self):
        # XXX not so sure about this feature, it's more an implementation
        # detail but not sure how to deal with deleting pending subscriptions
        # really...
        self.assertEquals({}, self.folder._pending)
        self.folder.start_subscription('foo@bar.com', True)
        self.assert_('foo@bar.com' in self.folder._pending)
        self.folder.start_subscription('foo@bar.com', False)
        self.assertEquals({}, self.folder._pending)

    def test_finish_subscription_wrong_vernum(self):
        self.folder.start_subscription('foo@bar.com', True)
        vernum = self.verreg.search(self.mailhost.sent[0][2]).group(1)
        self.assertRaises(ValueError, self.folder.finish_subscription,
                          'foo@bar.com', vernum + 'abcd')

    def test_finish_subscription_non_existing_address(self):
        self.assertRaises(KeyError, self.folder.finish_subscription,
                          'foo@bar.com', '12345678')

# some utility functions and classes
class FakeMaildropHost(object):
    def __init__(self):
        self.sent = []

    def _send(self, mfro, mto, body):
        self.sent.append((mfro, mto, body))

def normalize_getvars(getvars):
    return dict([(k, isinstance(v, list) and v or [v]) for
                 (k, v) in getvars.iteritems()])

def spliturl(url):
    if '?' not in url:
        return url, {}
    url, query = url.split('?', 1)
    return url, cgi.parse_qs(query)

def sorted(l, sortfunc=cmp):
    l.sort(sortfunc)
    return l

def test_suite():
    suite = unittest.TestSuite()
    suite.addTest(unittest.makeSuite(MailingFolderTest))
    return suite

