# -*- coding: utf-8 -*-
from DateTime import DateTime
from ftw.solr.config import SolrConfig
from ftw.solr.connection import SolrResponse
from ftw.solr.handlers import ATBlobFileIndexHandler
from ftw.solr.handlers import DefaultIndexHandler
from ftw.solr.handlers import DexterityItemIndexHandler
from ftw.solr.schema import SolrSchema
from ftw.solr.testing import FTW_SOLR_AT_INTEGRATION_TESTING
from ftw.solr.testing import FTW_SOLR_INTEGRATION_TESTING
from ftw.solr.tests.utils import get_data
from ftw.solr.tests.utils import normalize_whitespaces
from mock import call
from mock import MagicMock
from mock import PropertyMock
from plone import api
from plone.app.testing import login
from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID
from plone.app.testing import TEST_USER_NAME
from plone.namedfile.file import NamedBlobFile
from plone.uuid.interfaces import IMutableUUID
from Products.CMFPlone.utils import base_hasattr
from Products.CMFPlone.utils import getFSVersionTuple

import unittest


class TestDefaultIndexHandler(unittest.TestCase):

    layer = FTW_SOLR_INTEGRATION_TESTING

    def setUp(self):
        self.portal = self.layer['portal']
        login(self.portal, TEST_USER_NAME)
        setRoles(self.portal, TEST_USER_ID, ['Manager'])

        self.doc = api.content.create(
            type='Document', title='My Document', id='doc',
            container=self.portal)
        if base_hasattr(self.doc, '_setUID'):
            self.doc._setUID('09baa75b67f44383880a6dab8b3200b6')
            self.doc.setModificationDate(DateTime('2017-01-21T17:18:19+00:00'))
        else:
            IMutableUUID(self.doc).set('09baa75b67f44383880a6dab8b3200b6')
            self.doc.modification_date = DateTime('2017-01-21T17:18:19+00:00')

        conn = MagicMock(name='SolrConnection')
        conn.get = MagicMock(name='get', return_value=SolrResponse(
            body=get_data('schema.json'), status=200))
        self.manager = MagicMock(name='SolrConnectionManager')
        type(self.manager).connection = PropertyMock(return_value=conn)
        type(self.manager).schema = PropertyMock(return_value=SolrSchema(
            self.manager))
        self.handler = DefaultIndexHandler(self.doc, self.manager)

    def test_get_data_without_attributes_gets_all_fields(self):
        data = self.handler.get_data(None)
        data['SearchableText'] = normalize_whitespaces(data['SearchableText'])
        self.assertEqual(
            {
                u'Title': u'My Document',
                u'UID': u'09baa75b67f44383880a6dab8b3200b6',
                u'allowedRolesAndUsers': [u'Anonymous'],
                u'path': u'/plone/doc',
                u'path_depth': 2,
                u'modified': u'2017-01-21T17:18:19.000Z',
                u'SearchableText': 'doc My Document',
            },
            data
        )

    def test_get_data_gets_only_specified_attributes(self):
        self.assertEqual(
            {
                u'UID': u'09baa75b67f44383880a6dab8b3200b6',
                u'modified': u'2017-01-21T17:18:19.000Z',
            },
            self.handler.get_data(['UID', 'modified'])
        )

    def test_get_data_always_includes_unique_key(self):
        self.assertEqual(
            {
                u'Title': u'My Document',
                u'UID': u'09baa75b67f44383880a6dab8b3200b6',
            },
            self.handler.get_data(['Title'])
        )

    def test_get_data_includes_path_depth_if_exists_and_path_was_included(self):
        self.assertEqual(
            {
                u'path': u'/plone/doc',
                u'path_depth': 2,
                u'UID': u'09baa75b67f44383880a6dab8b3200b6',
            },
            self.handler.get_data(['path'])
        )

        # solr schemas without the path_depth field
        self.manager.schema.fields.pop('path_depth')
        self.assertEqual(
            {
                u'path': u'/plone/doc',
                u'UID': u'09baa75b67f44383880a6dab8b3200b6',
            },
            self.handler.get_data(['path'])
        )

    def test_add_without_attributes_adds_full_documemt(self):
        self.manager.connection.add = MagicMock(name='add')
        self.handler.add(None)
        data = self.manager.connection.add.call_args[0][0]
        data['SearchableText'] = normalize_whitespaces(data['SearchableText'])
        self.assertEqual(
            {
                u'UID': u'09baa75b67f44383880a6dab8b3200b6',
                u'Title': u'My Document',
                u'modified': u'2017-01-21T17:18:19.000Z',
                u'SearchableText': 'doc My Document',
                u'allowedRolesAndUsers': [u'Anonymous'],
                u'path': u'/plone/doc',
                u'path_depth': 2,
            },
            data
        )

    def test_add_with_attributes_does_atomic_update(self):
        self.manager.connection.add = MagicMock(name='add')
        self.handler.add(['Title', 'modified'])
        self.manager.connection.add.assert_called_once_with({
            u'UID': u'09baa75b67f44383880a6dab8b3200b6',
            u'Title': {'set': u'My Document'},
            u'modified': {'set': u'2017-01-21T17:18:19.000Z'},
        })

    def test_add_with_attributes_without_data_does_nothing(self):
        self.manager.connection.add = MagicMock(name='add')
        self.handler.add(['field_not_in_schema'])
        self.assertFalse(self.manager.connection.add.called)

    def test_delete(self):
        self.manager.connection.delete = MagicMock(name='delete')
        self.handler.delete()
        self.manager.connection.delete.assert_called_once_with(
            u'09baa75b67f44383880a6dab8b3200b6')


class TestDefaultIndexHandlerWithLangIdProcessor(unittest.TestCase):

    layer = FTW_SOLR_INTEGRATION_TESTING

    def setUp(self):
        self.portal = self.layer['portal']
        login(self.portal, TEST_USER_NAME)
        setRoles(self.portal, TEST_USER_ID, ['Manager'])

        self.doc = api.content.create(
            type='Document', title='My Document', id='doc',
            container=self.portal)
        if base_hasattr(self.doc, '_setUID'):
            self.doc._setUID('09baa75b67f44383880a6dab8b3200b6')
            self.doc.setModificationDate(DateTime('2017-01-21T17:18:19+00:00'))
        else:
            IMutableUUID(self.doc).set('09baa75b67f44383880a6dab8b3200b6')
            self.doc.modification_date = DateTime('2017-01-21T17:18:19+00:00')

        conn = MagicMock(name='SolrConnection')
        conn.get = MagicMock(name='get', return_value=SolrResponse(
            body=get_data('schema_langid.json'), status=200))
        self.manager = MagicMock(name='SolrConnectionManager')
        type(self.manager).connection = PropertyMock(return_value=conn)
        type(self.manager).schema = PropertyMock(return_value=SolrSchema(
            self.manager))
        conn.get = MagicMock(name='get', return_value=SolrResponse(
            body=get_data('config_langid.json'), status=200))
        type(self.manager).config = PropertyMock(return_value=SolrConfig(
            self.manager))
        self.handler = DefaultIndexHandler(self.doc, self.manager)

    def test_all_indexable_fields_does_not_include_lang_fields(self):
        fields = self.handler.all_indexable_fields()
        self.assertIn(u'Title', fields)
        self.assertNotIn(u'Title_de', fields)
        self.assertNotIn(u'Title_en', fields)
        self.assertNotIn(u'Title_fr', fields)
        self.assertNotIn(u'Title_general', fields)

    def test_add_lang_field_deletes_all_language_variants(self):
        self.manager.connection.add = MagicMock(name='add')
        self.handler.add(['Title', 'modified'])
        expected = [
            call({
                u'Title_de': {u'set': None},
                u'Title_en': {u'set': None},
                u'Title_fr': {u'set': None},
                u'Title_general': {u'set': None},
                u'UID': u'09baa75b67f44383880a6dab8b3200b6',
            }),
            call({
                u'UID': u'09baa75b67f44383880a6dab8b3200b6',
                u'modified': {u'set': u'2017-01-21T17:18:19.000Z'},
                u'Title': {u'set': u'My Document'},
            }),
        ]
        assert self.manager.connection.add.call_args_list == expected


@unittest.skipIf(getFSVersionTuple() >= (5, 0),
                 'Files are not AT-based on Plone 5 and later')
class TestATBlobFileIndexHandler(unittest.TestCase):

    layer = FTW_SOLR_AT_INTEGRATION_TESTING

    def setUp(self):
        self.portal = self.layer['portal']
        login(self.portal, TEST_USER_NAME)
        setRoles(self.portal, TEST_USER_ID, ['Manager'])

        self.doc = api.content.create(
            type='File', title='My File', id='doc',
            file='File data ...', container=self.portal)

        field = self.doc.getPrimaryField().get(self.doc)
        field.setFilename('document.docx')
        field.setContentType('application/vnd.openxmlformats-officedocument.wordprocessingml.document')

        if base_hasattr(self.doc, '_setUID'):
            self.doc._setUID('09baa75b67f44383880a6dab8b3200b6')
            self.doc.setModificationDate(DateTime('2017-01-21T17:18:19+00:00'))
        else:
            IMutableUUID(self.doc).set('09baa75b67f44383880a6dab8b3200b6')
            self.doc.modification_date = DateTime('2017-01-21T17:18:19+00:00')

        conn = MagicMock(name='SolrConnection')
        conn.get = MagicMock(name='get', return_value=SolrResponse(
            body=get_data('schema.json'), status=200))
        self.manager = MagicMock(name='SolrConnectionManager')
        type(self.manager).connection = PropertyMock(return_value=conn)
        type(self.manager).schema = PropertyMock(return_value=SolrSchema(
            self.manager))
        self.handler = ATBlobFileIndexHandler(self.doc, self.manager)

    def test_add_without_attributes_calls_add_and_extract(self):
        self.manager.connection.add = MagicMock(name='add')
        self.manager.connection.extract = MagicMock(name='extract')
        self.handler.add(None)
        self.manager.connection.add.assert_called_once_with({
            u'UID': u'09baa75b67f44383880a6dab8b3200b6',
            u'Title': {u'set': u'My File'},
            u'modified': {u'set': u'2017-01-21T17:18:19.000Z'},
            u'allowedRolesAndUsers': {u'set': [u'Anonymous']},
            u'path': {u'set': u'/plone/doc'},
            u'path_depth': {u'set': 2},
        })
        self.manager.connection.extract.assert_called_once_with(
            self.doc.getFile().blob,
            'SearchableText',
            {u'UID': u'09baa75b67f44383880a6dab8b3200b6'},
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
        )

    def test_add_with_attributes_without_searchabletext_calls_add(self):
        self.manager.connection.add = MagicMock(name='add')
        self.manager.connection.extract = MagicMock(name='extract')
        self.handler.add(['Title', 'modified'])
        self.manager.connection.add.assert_called_once_with({
            u'UID': u'09baa75b67f44383880a6dab8b3200b6',
            u'Title': {'set': u'My File'},
            u'modified': {'set': u'2017-01-21T17:18:19.000Z'},
        })
        self.manager.connection.extract.assert_not_called()

    def test_add_with_attributes_with_searchabletext_calls_add_and_extract(self):
        self.manager.connection.add = MagicMock(name='add')
        self.manager.connection.extract = MagicMock(name='extract')
        self.handler.add(['SearchableText', 'modified'])
        self.manager.connection.add.assert_called_once_with({
            u'UID': u'09baa75b67f44383880a6dab8b3200b6',
            u'modified': {'set': u'2017-01-21T17:18:19.000Z'},
        })
        self.manager.connection.extract.assert_called_once_with(
            self.doc.getFile().blob,
            'SearchableText',
            {u'UID': u'09baa75b67f44383880a6dab8b3200b6'},
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
        )

    def test_add_with_searchabletext_only_calls_extract(self):
        self.manager.connection.add = MagicMock(name='add')
        self.manager.connection.extract = MagicMock(name='extract')
        self.handler.add(['SearchableText'])
        self.manager.connection.add.assert_not_called()
        self.manager.connection.extract.assert_called_once_with(
            self.doc.getFile().blob,
            'SearchableText',
            {u'UID': u'09baa75b67f44383880a6dab8b3200b6'},
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
        )

    def test_add_with_attributes_without_data_does_nothing(self):
        self.manager.connection.add = MagicMock(name='add')
        self.handler.add(['field_not_in_schema'])
        self.assertFalse(self.manager.connection.add.called)

    def test_add_with_attributes_as_tuple_with_searchabletext(self):
        self.manager.connection.add = MagicMock(name='add')
        self.manager.connection.extract = MagicMock(name='extract')

        self.handler.add(('SearchableText', 'modified'))
        self.manager.connection.add.assert_called_once_with({
            u'UID': u'09baa75b67f44383880a6dab8b3200b6',
            u'modified': {'set': u'2017-01-21T17:18:19.000Z'},
        })
        self.manager.connection.extract.assert_called_once_with(
            self.doc.getFile().blob,
            'SearchableText',
            {u'UID': u'09baa75b67f44383880a6dab8b3200b6'},
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
        )

    def test_add_with_only_searchable_text_and_uid_calls_extract(self):
        self.manager.connection.add = MagicMock(name='add')
        self.manager.connection.extract = MagicMock(name='extract')

        self.handler.add(('SearchableText', 'UID'))
        self.manager.connection.add.assert_not_called()
        self.manager.connection.extract.assert_called_once_with(
            self.doc.getFile().blob,
            'SearchableText',
            {u'UID': u'09baa75b67f44383880a6dab8b3200b6'},
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
        )


class TestDexterityItemIndexHandler(unittest.TestCase):

    layer = FTW_SOLR_INTEGRATION_TESTING

    def setUp(self):
        self.portal = self.layer['portal']
        login(self.portal, TEST_USER_NAME)
        setRoles(self.portal, TEST_USER_ID, ['Manager'])

        namedfile = NamedBlobFile(
            data='File data ...',
            filename=u'document.docx',
            contentType='application/vnd.openxmlformats-officedocument.wordprocessingml.document')
        self.doc = api.content.create(
            type='File', title='My File', id='doc',
            file=namedfile, container=self.portal)
        if base_hasattr(self.doc, '_setUID'):
            self.doc._setUID('09baa75b67f44383880a6dab8b3200b6')
            self.doc.setModificationDate(DateTime('2017-01-21T17:18:19+00:00'))
        else:
            IMutableUUID(self.doc).set('09baa75b67f44383880a6dab8b3200b6')
            self.doc.modification_date = DateTime('2017-01-21T17:18:19+00:00')

        conn = MagicMock(name='SolrConnection')
        conn.get = MagicMock(name='get', return_value=SolrResponse(
            body=get_data('schema.json'), status=200))
        self.manager = MagicMock(name='SolrConnectionManager')
        type(self.manager).connection = PropertyMock(return_value=conn)
        type(self.manager).schema = PropertyMock(return_value=SolrSchema(
            self.manager))
        self.handler = DexterityItemIndexHandler(self.doc, self.manager)

        self.manager.connection.add = MagicMock(name='add')
        self.manager.connection.extract = MagicMock(name='extract')

    def test_add_without_attributes_calls_add_and_extract(self):
        self.handler.add(None)
        self.manager.connection.add.assert_called_once_with({
            u'UID': u'09baa75b67f44383880a6dab8b3200b6',
            u'Title': {u'set': u'My File'},
            u'modified': {u'set': u'2017-01-21T17:18:19.000Z'},
            u'allowedRolesAndUsers': {u'set': [u'Anonymous']},
            u'path': {u'set': u'/plone/doc'},
            u'path_depth': {u'set': 2},
        })
        self.manager.connection.extract.assert_called_once_with(
            self.doc.file._blob,
            'SearchableText',
            {u'UID': u'09baa75b67f44383880a6dab8b3200b6'},
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
        )

    def test_add_with_empty_list_attributes_calls_add_and_extract(self):
        self.handler.add([])
        self.manager.connection.add.assert_called_once_with({
            u'UID': u'09baa75b67f44383880a6dab8b3200b6',
            u'Title': {u'set': u'My File'},
            u'modified': {u'set': u'2017-01-21T17:18:19.000Z'},
            u'allowedRolesAndUsers': {u'set': [u'Anonymous']},
            u'path': {u'set': u'/plone/doc'},
            u'path_depth': {u'set': 2},
        })
        self.manager.connection.extract.assert_called_once_with(
            self.doc.file._blob,
            'SearchableText',
            {u'UID': u'09baa75b67f44383880a6dab8b3200b6'},
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
        )

    def test_add_with_attributes_without_searchabletext_calls_add(self):
        self.handler.add(['Title', 'modified'])
        self.manager.connection.add.assert_called_once_with({
            u'UID': u'09baa75b67f44383880a6dab8b3200b6',
            u'Title': {'set': u'My File'},
            u'modified': {'set': u'2017-01-21T17:18:19.000Z'},
        })
        self.manager.connection.extract.assert_not_called()

    def test_add_with_attributes_with_searchabletext_calls_add_and_extract(self):
        self.handler.add(['SearchableText', 'modified'])
        self.manager.connection.add.assert_called_once_with({
            u'UID': u'09baa75b67f44383880a6dab8b3200b6',
            u'modified': {'set': u'2017-01-21T17:18:19.000Z'},
        })
        self.manager.connection.extract.assert_called_once_with(
            self.doc.file._blob,
            'SearchableText',
            {u'UID': u'09baa75b67f44383880a6dab8b3200b6'},
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
        )

    def test_add_with_searchabletext_only_calls_extract(self):
        self.handler.add(['SearchableText'])
        self.manager.connection.add.assert_not_called()
        self.manager.connection.extract.assert_called_once_with(
            self.doc.file._blob,
            'SearchableText',
            {u'UID': u'09baa75b67f44383880a6dab8b3200b6'},
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
        )

    def test_add_without_attributes_for_item_without_blob_calls_add(self):
        item = api.content.create(
            type='Document', title='My Document', id='doc2',
            container=self.portal)
        IMutableUUID(item).set('56d2c8b62c064f38890c60b66d01c894')
        item.modification_date = DateTime('2017-01-21T17:18:19+00:00')

        handler = DexterityItemIndexHandler(item, self.manager)
        handler.add(None)
        self.manager.connection.add.assert_called_once_with({
            u'UID': u'56d2c8b62c064f38890c60b66d01c894',
            u'Title': {u'set': u'My Document'},
            u'modified': {u'set': u'2017-01-21T17:18:19.000Z'},
            u'SearchableText': {'set': ' doc2 My Document   '},
            u'allowedRolesAndUsers': {u'set': [u'Anonymous']},
            u'path': {u'set': u'/plone/doc2'},
            u'path_depth': {u'set': 2},
        })
        self.manager.connection.extract.assert_not_called()

    def test_add_with_attributes_for_item_without_blob_calls_add(self):
        item = api.content.create(
            type='Document', title='My Document', id='doc2',
            container=self.portal)
        IMutableUUID(item).set('56d2c8b62c064f38890c60b66d01c894')
        item.modification_date = DateTime('2017-01-21T17:18:19+00:00')

        handler = DexterityItemIndexHandler(item, self.manager)
        handler.add(['SearchableText', 'modified'])
        self.manager.connection.add.assert_called_once_with({
            u'UID': u'56d2c8b62c064f38890c60b66d01c894',
            u'modified': {u'set': u'2017-01-21T17:18:19.000Z'},
            u'SearchableText': {'set': ' doc2 My Document   '},
        })
        self.manager.connection.extract.assert_not_called()

    def test_add_with_attributes_without_data_does_nothing(self):
        self.manager.connection.add = MagicMock(name='add')
        self.handler.add(['field_not_in_schema'])
        self.assertFalse(self.manager.connection.add.called)

    def test_add_with_attributes_as_tuple_with_searchabletext(self):
        self.handler.add(('SearchableText', 'modified'))
        self.manager.connection.add.assert_called_once_with({
            u'UID': u'09baa75b67f44383880a6dab8b3200b6',
            u'modified': {'set': u'2017-01-21T17:18:19.000Z'},
        })
        self.manager.connection.extract.assert_called_once_with(
            self.doc.file._blob,
            'SearchableText',
            {u'UID': u'09baa75b67f44383880a6dab8b3200b6'},
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
        )

    def test_add_with_only_searchable_text_and_uid_calls_extract(self):
        self.handler.add(('SearchableText', 'UID'))
        self.manager.connection.add.assert_not_called()
        self.manager.connection.extract.assert_called_once_with(
            self.doc.file._blob,
            'SearchableText',
            {u'UID': u'09baa75b67f44383880a6dab8b3200b6'},
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
        )
