from DateTime import DateTime
from plone import api
from plone.registry.interfaces import IRegistry
from ploneintranet.microblog import statusupdate
from ploneintranet.microblog.browser.extract import StatusupdateExtractAttachments
from ploneintranet.microblog.browser.extract import StreamExtractAttachments
from ploneintranet.microblog.interfaces import IMicroblogContext
from ploneintranet.microblog.interfaces import IMicroblogTool
from ploneintranet.microblog.testing import PLONEINTRANET_MICROBLOG_INTEGRATION_TESTING
from zope.component import queryUtility
from zope.interface import directlyProvides

import os
import unittest


class StatusUpdate(statusupdate.StatusUpdate):
    """Override actual implementation with unittest features"""

    def __init__(
        self, text, userid="dude", microblog_context=None, content_context=None
    ):
        self.userid = userid
        self.creator = userid
        super().__init__(
            text, microblog_context=microblog_context, content_context=content_context
        )

    def _init_userid(self):
        pass

    def _init_creator(self):
        pass

    def _init_microblog_context(
        self, thread_id, microblog_context=None, content_context=None
    ):
        self._m_context = microblog_context
        self._microblog_context_uuid = self._context2uuid(microblog_context)

    def _init_content_context(self, thread_id, content_context):
        self._c_context = content_context
        self._content_context_uuid = self._context2uuid(content_context)

    @property
    def microblog_context(self):
        return self._m_context

    @property
    def content_context(self):
        return self._c_context


class ExtractAttachmentsBase(unittest.TestCase):

    layer = PLONEINTRANET_MICROBLOG_INTEGRATION_TESTING

    odt_name = "bärtige_flößer.odt"
    simple_odt_name = "bartige_flosser.odt"
    png_name = "vision-to-product.png"

    def setUp(self):
        self.app = self.layer["app"]
        self.portal = self.layer["portal"]
        self.request = self.portal.REQUEST
        self.portal_workflow = api.portal.get_tool("portal_workflow")
        self.mock_workspace1 = api.content.create(self.portal, "Folder", "workspace1")
        directlyProvides(self.mock_workspace1, IMicroblogContext)
        self.mock_workspace1.reindexObject()
        self.mock_workspace2 = api.content.create(self.portal, "Folder", "workspace2")
        directlyProvides(self.mock_workspace2, IMicroblogContext)
        self.mock_workspace2.reindexObject()
        # this needs to happen AFTER the directlyProvides, dunno why
        self.mock_folder = api.content.create(
            self.mock_workspace2, "Folder", "my-folder"
        )
        self.statuscontainer = queryUtility(IMicroblogTool)
        # provide the default user for tests
        api.user.create("dude@example.org", "dude")

    def file_data(self, filename):
        path = os.path.join(os.path.dirname(__file__), "uploads", filename)
        with open(path, "rb") as fh:
            data = fh.read()
        return data

    @property
    def odt_data(self):
        return self.file_data(self.odt_name)

    @property
    def png_data(self):
        return self.file_data(self.png_name)


class StreamExtractAttachmentsTestCase(ExtractAttachmentsBase):
    def setUp(self):
        super().setUp()
        self.view = StreamExtractAttachments(self.portal, self.request)

    def test_view_call(self):
        result = self.view()
        self.assertEqual("Extracted 0 attachments.", result)

    def test_get_statusupdates_sitewide(self):
        su1 = StatusUpdate("A", microblog_context=self.mock_workspace1)
        su2 = StatusUpdate("B")
        su3 = StatusUpdate("C", microblog_context=self.mock_workspace2)
        self.statuscontainer.add(su1)
        self.statuscontainer.add(su2)
        self.statuscontainer.add(su3)
        self.assertEqual([su3, su1], [x for x in self.view.get_statusupdates()])

    def test_get_statusupdates_workspace(self):
        su1 = StatusUpdate("A", microblog_context=self.mock_workspace1)
        su2 = StatusUpdate("B")
        su3 = StatusUpdate("C", microblog_context=self.mock_workspace2)
        self.statuscontainer.add(su1)
        self.statuscontainer.add(su2)
        self.statuscontainer.add(su3)
        view = StreamExtractAttachments(self.mock_workspace1, self.request)
        self.assertEqual([su1], [x for x in view.get_statusupdates()])

    def test_extract_attachment_png(self):
        su = StatusUpdate("A", microblog_context=self.mock_workspace1)
        su.add_attachment(self.png_name, self.png_data)
        self.statuscontainer.add(su)
        num_attachments = self.view.extract()
        self.assertEqual(1, num_attachments)
        # the original stream attachment is removed
        self.assertEqual([], [x for x in su.attachments.keys()])

    def test_extract_attachment_odt(self):
        su = StatusUpdate("A", microblog_context=self.mock_workspace1)
        su.add_attachment(self.odt_name, self.odt_data)
        self.statuscontainer.add(su)
        num_attachments = self.view.extract()
        self.assertEqual(1, num_attachments)
        # the original stream attachment is removed
        self.assertEqual([], [x for x in su.attachments.keys()])

    def test_extract_ignores_global_statusupdate(self):
        su = StatusUpdate("A")
        su.add_attachment(self.odt_name, self.odt_data)
        self.statuscontainer.add(su)
        num_attachments = self.view.extract()
        self.assertEqual(0, num_attachments)

    def test_extract_ignores_statusupdate_without_attachment(self):
        su = StatusUpdate("A", microblog_context=self.mock_workspace1)
        self.statuscontainer.add(su)
        num_attachments = self.view.extract()
        self.assertEqual(0, num_attachments)

    def test_extract_ignores_content_updates(self):
        su = StatusUpdate(
            "A",
            microblog_context=self.mock_workspace1,
            content_context=self.mock_folder,
        )
        su.add_attachment(self.odt_name, self.odt_data)
        self.statuscontainer.add(su)
        num_attachments = self.view.extract()
        self.assertEqual(0, num_attachments)

    def test_extraction_creates_incoming_folder(self):
        su = StatusUpdate("A", microblog_context=self.mock_workspace1)
        su.add_attachment(self.odt_name, self.odt_data)
        self.statuscontainer.add(su)
        num_attachments = self.view.extract()
        self.assertEqual(1, num_attachments)
        self.assertIn("INCOMING", self.mock_workspace1)

    def test_extraction_creates_incoming_file(self):
        su = StatusUpdate("A", microblog_context=self.mock_workspace1)
        su.add_attachment(self.odt_name, self.odt_data)
        self.statuscontainer.add(su)
        num_attachments = self.view.extract()
        self.assertEqual(1, num_attachments)
        self.assertIn("INCOMING", self.mock_workspace1)
        self.assertIn(self.simple_odt_name, self.mock_workspace1.INCOMING)
        content = self.mock_workspace1.INCOMING[self.simple_odt_name]
        self.assertEqual(self.odt_name, content.file.filename)
        self.assertEqual(self.odt_data, content.file.data)

    def test_extraction_creates_incoming_image(self):
        su = StatusUpdate("A", microblog_context=self.mock_workspace1)
        su.add_attachment(self.png_name, self.png_data)
        self.statuscontainer.add(su)
        num_attachments = self.view.extract()
        self.assertEqual(1, num_attachments)
        self.assertIn("INCOMING", self.mock_workspace1)
        self.assertIn(self.png_name, self.mock_workspace1.INCOMING)
        content = self.mock_workspace1.INCOMING[self.png_name]
        self.assertEqual(self.png_name, content.image.filename)
        self.assertEqual(self.png_data, content.image.data)

    def test_extraction_content_context(self):
        su = StatusUpdate("A", microblog_context=self.mock_workspace1)
        su.add_attachment(self.odt_name, self.odt_data)
        self.statuscontainer.add(su)
        self.view.extract()
        content = self.mock_workspace1.INCOMING[self.simple_odt_name]
        self.assertEqual(content, su.content_context)

    def test_extraction_content_context_indexed(self):
        su = StatusUpdate("A", microblog_context=self.mock_workspace1)
        su.add_attachment(self.odt_name, self.odt_data)
        self.statuscontainer.add(su)
        self.view.extract()
        content = self.mock_workspace1.INCOMING[self.simple_odt_name]
        self.assertEqual(
            [su], [x for x in self.statuscontainer.content_values(content)]
        )

    def test_extraction_metadata_title(self):
        su = StatusUpdate("A", microblog_context=self.mock_workspace1)
        su.add_attachment(self.odt_name, self.odt_data)
        self.statuscontainer.add(su)
        self.view.extract()
        content = self.mock_workspace1.INCOMING[self.simple_odt_name]
        self.assertEqual(self.odt_name, content.title)

    def test_extraction_metadata_creator(self):
        api.user.create("killroy@example.org", "killroy")
        su = StatusUpdate("A", microblog_context=self.mock_workspace1, userid="killroy")
        su.add_attachment(self.odt_name, self.odt_data)
        self.statuscontainer.add(su)
        self.view.extract()
        content = self.mock_workspace1.INCOMING[self.simple_odt_name]
        self.assertEqual(("killroy",), content.creators)

    def test_extraction_metadata_created(self):
        su = StatusUpdate("A", microblog_context=self.mock_workspace1)
        su.add_attachment(self.odt_name, self.odt_data)
        su.date = DateTime(2015, 1, 1)
        self.statuscontainer.add(su)
        self.view.extract()
        content = self.mock_workspace1.INCOMING[self.simple_odt_name]
        self.assertEqual(su.date, content.created())

    def test_extraction_metadata_owner(self):
        killroy = api.user.create("killroy@example.org", "killroy")
        su = StatusUpdate("A", microblog_context=self.mock_workspace1, userid="killroy")
        su.add_attachment(self.odt_name, self.odt_data)
        self.statuscontainer.add(su)
        self.view.extract()
        content = self.mock_workspace1.INCOMING[self.simple_odt_name]
        self.assertEqual("killroy", content.getOwner().getId())
        self.assertIn("Owner", api.user.get_roles(username="killroy", obj=content))
        self.assertIn("Owner", api.user.get_roles(user=killroy, obj=content))


class StatusupdateExtractAttachmentsTestCase(ExtractAttachmentsBase):
    def setUp(self):
        super().setUp()

    def test_registry_extract_auto_default_false(self):
        registry = queryUtility(IRegistry)
        enabled = registry["ploneintranet.microblog.extract.auto"]
        self.assertFalse(enabled)  # would raise KeyError if record was missing

    def test_registry_extract_folder_default_INCOMING(self):
        registry = queryUtility(IRegistry)
        folder = registry["ploneintranet.microblog.extract.folder.id"]
        self.assertEqual(folder, "INCOMING")

    def test_registry_set_extract_folder_id_acii(self):
        registry = queryUtility(IRegistry)
        registry["ploneintranet.microblog.extract.folder.id"] = "STREAM"
        folder = registry["ploneintranet.microblog.extract.folder.id"]
        self.assertEqual(folder, "STREAM")

    def test_registry_set_extract_folder_unicode_disallowed(self):
        """It's a id, not a title. KISS."""

        registry = queryUtility(IRegistry)
        from zope.schema.interfaces import InvalidValue

        with self.assertRaises(InvalidValue):
            registry["ploneintranet.microblog.extract.folder.id"] = "STRöM"

    def test_registry_set_extract_folder_title_unicode(self):
        registry = queryUtility(IRegistry)
        registry["ploneintranet.microblog.extract.folder.title"] = "STRöM"
        folder = registry["ploneintranet.microblog.extract.folder.title"]
        self.assertEqual(folder, "STRöM")

    def test_extraction_creates_proper_extract_folder(self):
        registry = queryUtility(IRegistry)
        registry["ploneintranet.microblog.extract.folder.id"] = "STREAM"
        registry["ploneintranet.microblog.extract.folder.title"] = "STRöM"

        su = StatusUpdate("A", microblog_context=self.mock_workspace1)
        view = StatusupdateExtractAttachments(su, self.request)
        view.get_or_create_extract_folder(su)
        self.assertIn("STREAM", self.mock_workspace1)
