# -*- coding: utf-8; -*-
#
# This file is part of Superdesk.
#
# Copyright 2013, 2014 Sourcefabric z.u. and contributors.
#
# For the full copyright and license information, please see the
# AUTHORS and LICENSE files distributed with this source code, or
# at https://www.sourcefabric.org/superdesk/license

from eve.utils import ParsedRequest
import json
import os

from apps.content_filters.content_filter.content_filter_service import ContentFilterService
from apps.prepopulate.app_populate import AppPopulateCommand
from superdesk import get_backend, get_resource_service
from superdesk.errors import SuperdeskApiError
from superdesk.publish import SubscribersService
from superdesk.tests import TestCase


class ContentFilterTests(TestCase):

    def setUp(self):
        self.req = ParsedRequest()
        with self.app.test_request_context(self.app.config.get('URL_PREFIX')):
            self.f = ContentFilterService(datasource='content_filters', backend=get_backend())
            self.s = SubscribersService(datasource='subscribers', backend=get_backend())

            self.articles = [{'_id': '1', 'urgency': 1, 'headline': 'story', 'state': 'fetched'},
                             {'_id': '2', 'headline': 'prtorque', 'state': 'fetched'},
                             {'_id': '3', 'urgency': 3, 'headline': 'creator', 'state': 'fetched'},
                             {'_id': '4', 'urgency': 4, 'state': 'fetched'},
                             {'_id': '5', 'urgency': 2, 'state': 'fetched'},
                             {'_id': '6', 'state': 'fetched'},
                             {'_id': '7', 'subject': [{'scheme': 'my_vocabulary', 'qcode': 'MV:01'}]},
                             {'_id': '8', 'extra': {'custom_text': 'my text'}}]
            self.app.data.insert('archive', self.articles)

            self.app.data.insert('vocabularies',
                                 [{'_id': 'my_vocabulary',
                                   'display_name': 'My Vocabulary',
                                   'type': 'manageable',
                                   'field_type': None,
                                   'schema': {'name': {}, 'qcode': {}, 'parent': {}},
                                   'items': [{'name': 'option 1', 'qcode': 'MV:01', 'is_active': True}]
                                   },
                                  {'_id': 'custom_text',
                                   'display_name': 'Custom Text',
                                   'type': 'manageable',
                                   'field_type': 'text'}])

            self.app.data.insert('filter_conditions',
                                 [{'_id': 1,
                                   'field': 'headline',
                                   'operator': 'like',
                                   'value': 'tor',
                                   'name': 'test-1'}])
            self.app.data.insert('filter_conditions',
                                 [{'_id': 2,
                                   'field': 'urgency',
                                   'operator': 'in',
                                   'value': '2',
                                   'name': 'test-2'}])
            self.app.data.insert('filter_conditions',
                                 [{'_id': 3,
                                   'field': 'headline',
                                   'operator': 'endswith',
                                   'value': 'tor',
                                   'name': 'test-3'}])
            self.app.data.insert('filter_conditions',
                                 [{'_id': 4,
                                   'field': 'urgency',
                                   'operator': 'in',
                                   'value': '2,3,4',
                                   'name': 'test-4'}])
            self.app.data.insert('filter_conditions',
                                 [{'_id': 5,
                                   'field': 'headline',
                                   'operator': 'startswith',
                                   'value': 'sto',
                                   'name': 'test-5'}])
            self.app.data.insert('filter_conditions',
                                 [{'_id': 6,
                                   'field': 'my_vocabulary',
                                   'operator': 'in',
                                   'value': 'MV:01',
                                   'name': 'test-6'}])
            self.app.data.insert('filter_conditions',
                                 [{'_id': 7,
                                   'field': 'custom_text',
                                   'operator': 'eq',
                                   'value': 'my text',
                                   'name': 'test-7'}])

            self.app.data.insert('content_filters',
                                 [{"_id": 1,
                                   "content_filter": [{"expression": {"fc": [1]}}],
                                   "name": "soccer-only"}])

            self.app.data.insert('content_filters',
                                 [{"_id": 2,
                                   "content_filter": [{"expression": {"fc": [4, 3]}}],
                                   "name": "soccer-only2"}])

            self.app.data.insert('content_filters',
                                 [{"_id": 3,
                                   "content_filter": [{"expression": {"pf": [1], "fc": [2]}}],
                                   "name": "soccer-only3"}])

            self.app.data.insert('content_filters',
                                 [{"_id": 4,
                                   "content_filter": [{"expression": {"fc": [3]}}, {"expression": {"fc": [5]}}],
                                   "name": "soccer-only4"}])

            self.app.data.insert('content_filters',
                                 [{"_id": 5,
                                   "content_filter": [{"expression": {"fc": [6]}}],
                                   "name": "my-vocabulary"}])

            self.app.data.insert('content_filters',
                                 [{"_id": 6,
                                   "content_filter": [{"expression": {"fc": [7]}}],
                                   "name": "custom-text"}])

            self.app.data.insert('products',
                                 [{"_id": 1,
                                   "content_filter": {"filter_id": 3, "filter_type": "blocking"},
                                   "name": "p-1"}])

            self.app.data.insert('products',
                                 [{"_id": 2,
                                   "content_filter": {"filter_id": 1, "filter_type": "blocking"},
                                   "name": "p-2"}])

            self.app.data.insert('subscribers',
                                 [{"_id": 1,
                                   "products": [1],
                                   "name": "sub1"}])
            self.app.data.insert('subscribers',
                                 [{"_id": 2,
                                   "products": [2],
                                   "name": "sub2"}])

            self.app.data.insert('routing_schemes', [
                {
                    "_id": 1,
                    "name": "routing_scheme_1",
                    "rules": [{
                        "filter": 4,
                        "name": "routing_rule_4",
                        "schedule": {
                            "day_of_week": ["MON"],
                            "hour_of_day_from": "0000",
                            "hour_of_day_to": "2355",
                        },
                        "actions": {
                            "fetch": [],
                            "publish": [],
                            "exit": False
                        }
                    }]
                }
            ])


class RetrievingDataTests(ContentFilterTests):

    def test_build_mongo_query_using_like_filter_single_fc(self):
        doc = {'content_filter': [{"expression": {"fc": [1]}}], 'name': 'pf-1'}
        with self.app.app_context():
            query = self.f.build_mongo_query(doc)
            docs = get_resource_service('archive').\
                get_from_mongo(req=self.req, lookup=query)
            doc_ids = [d['_id'] for d in docs]
            self.assertEqual(3, docs.count())
            self.assertTrue('1' in doc_ids)
            self.assertTrue('2' in doc_ids)
            self.assertTrue('3' in doc_ids)

    def test_build_mongo_query_using_like_filter_single_pf(self):
        doc = {'content_filter': [{"expression": {"pf": [1]}}], 'name': 'pf-1'}
        with self.app.app_context():
            query = self.f.build_mongo_query(doc)
            docs = get_resource_service('archive').\
                get_from_mongo(req=self.req, lookup=query)
            doc_ids = [d['_id'] for d in docs]
            self.assertEqual(3, docs.count())
            self.assertTrue('1' in doc_ids)
            self.assertTrue('2' in doc_ids)
            self.assertTrue('3' in doc_ids)

    def test_build_mongo_query_using_like_filter_multi_filter_condition(self):
        doc = {'content_filter': [{"expression": {"fc": [1]}}, {"expression": {"fc": [2]}}], 'name': 'pf-1'}
        with self.app.app_context():
            query = self.f.build_mongo_query(doc)
            docs = get_resource_service('archive').\
                get_from_mongo(req=self.req, lookup=query)
            doc_ids = [d['_id'] for d in docs]
            self.assertEqual(4, docs.count())
            self.assertTrue('1' in doc_ids)
            self.assertTrue('2' in doc_ids)
            self.assertTrue('5' in doc_ids)

    def test_build_mongo_query_using_like_filter_multi_pf(self):
        doc = {'content_filter': [{"expression": {"pf": [1]}}, {"expression": {"fc": [2]}}], 'name': 'pf-1'}
        with self.app.app_context():
            query = self.f.build_mongo_query(doc)
            docs = get_resource_service('archive').\
                get_from_mongo(req=self.req, lookup=query)
            doc_ids = [d['_id'] for d in docs]
            self.assertEqual(4, docs.count())
            self.assertTrue('1' in doc_ids)
            self.assertTrue('2' in doc_ids)
            self.assertTrue('5' in doc_ids)

    def test_build_mongo_query_using_like_filter_multi_filter_condition2(self):
        doc = {'content_filter': [{"expression": {"fc": [3, 4]}}], 'name': 'pf-1'}
        with self.app.app_context():
            query = self.f.build_mongo_query(doc)
            docs = get_resource_service('archive').\
                get_from_mongo(req=self.req, lookup=query)
            doc_ids = [d['_id'] for d in docs]
            self.assertEqual(1, docs.count())
            self.assertTrue('3' in doc_ids)

    def test_build_mongo_query_using_like_filter_multi_pf2(self):
        doc = {'content_filter': [{"expression": {"pf": [2]}}], 'name': 'pf-1'}
        with self.app.app_context():
            query = self.f.build_mongo_query(doc)
            docs = get_resource_service('archive').\
                get_from_mongo(req=self.req, lookup=query)
            doc_ids = [d['_id'] for d in docs]
            self.assertEqual(1, docs.count())
            self.assertTrue('3' in doc_ids)

    def test_build_mongo_query_using_like_filter_multi_condition3(self):
        doc = {'content_filter': [{"expression": {"fc": [3, 4]}}, {"expression": {"fc": [1, 2]}}], 'name': 'pf-1'}
        with self.app.app_context():
            query = self.f.build_mongo_query(doc)
            docs = get_resource_service('archive').\
                get_from_mongo(req=self.req, lookup=query)
            doc_ids = [d['_id'] for d in docs]
            self.assertEqual(1, docs.count())
            self.assertTrue('3' in doc_ids)

    def test_build_mongo_query_using_like_filter_multi_pf3(self):
        doc = {'content_filter': [{"expression": {"pf": [2]}}, {"expression": {"pf": [1], "fc": [2]}}], 'name': 'pf-1'}
        with self.app.app_context():
            query = self.f.build_mongo_query(doc)
            docs = get_resource_service('archive').\
                get_from_mongo(req=self.req, lookup=query)
            doc_ids = [d['_id'] for d in docs]
            self.assertEqual(1, docs.count())
            self.assertTrue('3' in doc_ids)

    def test_build_elastic_query_using_like_filter_single_filter_condition(self):
        doc = {'content_filter': [{"expression": {"fc": [1]}}], 'name': 'pf-1'}
        with self.app.app_context():
            query = {'query': {'filtered': {'query': self.f._get_elastic_query(doc)}}}
            self.req.args = {'source': json.dumps(query)}
            docs = get_resource_service('archive').get(req=self.req, lookup=None)
            doc_ids = [d['_id'] for d in docs]
            self.assertEqual(3, docs.count())
            self.assertTrue('1' in doc_ids)
            self.assertTrue('2' in doc_ids)
            self.assertTrue('3' in doc_ids)

    def test_build_elastic_query_using_like_filter_single_content_filter(self):
        doc = {'content_filter': [{"expression": {"pf": [1]}}], 'name': 'pf-1'}
        with self.app.app_context():
            query = {'query': {'filtered': {'query': self.f._get_elastic_query(doc)}}}
            self.req.args = {'source': json.dumps(query)}
            docs = get_resource_service('archive').get(req=self.req, lookup=None)
            doc_ids = [d['_id'] for d in docs]
            self.assertEqual(3, docs.count())
            self.assertTrue('1' in doc_ids)
            self.assertTrue('2' in doc_ids)
            self.assertTrue('3' in doc_ids)

    def test_build_elastic_query_using_like_filter_multi_filter_condition(self):
        doc = {'content_filter': [{"expression": {"fc": [1]}}, {"expression": {"fc": [2]}}], 'name': 'pf-1'}
        with self.app.app_context():
            query = {'query': {'filtered': {'query': self.f._get_elastic_query(doc)}}}
            self.req.args = {'source': json.dumps(query)}
            docs = get_resource_service('archive').get(req=self.req, lookup=None)
            doc_ids = [d['_id'] for d in docs]
            self.assertEqual(4, docs.count())
            self.assertTrue('1' in doc_ids)
            self.assertTrue('2' in doc_ids)
            self.assertTrue('3' in doc_ids)
            self.assertTrue('5' in doc_ids)

    def test_build_mongo_query_using_like_filter_multi_content_filter(self):
        doc = {'content_filter': [{"expression": {"pf": [1]}}, {"expression": {"fc": [2]}}], 'name': 'pf-1'}
        with self.app.app_context():
            query = {'query': {'filtered': {'query': self.f._get_elastic_query(doc)}}}
            self.req.args = {'source': json.dumps(query)}
            docs = get_resource_service('archive').get(req=self.req, lookup=None)
            doc_ids = [d['_id'] for d in docs]
            self.assertEqual(4, docs.count())
            self.assertTrue('1' in doc_ids)
            self.assertTrue('2' in doc_ids)
            self.assertTrue('3' in doc_ids)
            self.assertTrue('5' in doc_ids)

    def test_build_elastic_query_using_like_filter_multi_filter_condition2(self):
        doc = {'content_filter': [{"expression": {"fc": [3, 4]}}, {"expression": {"fc": [1, 2]}}], 'name': 'pf-1'}
        with self.app.app_context():
            query = {'query': {'filtered': {'query': self.f._get_elastic_query(doc)}}}
            self.req.args = {'source': json.dumps(query)}
            docs = get_resource_service('archive').get(req=self.req, lookup=None)
            doc_ids = [d['_id'] for d in docs]
            self.assertEqual(1, docs.count())
            self.assertTrue('3' in doc_ids)

    def test_build_elastic_query_using_like_filter_multi_content_filter2(self):
        doc = {'content_filter': [{"expression": {"fc": [4, 3]}},
                                  {"expression": {"pf": [1], "fc": [2]}}], 'name': 'pf-1'}
        with self.app.app_context():
            query = {'query': {'filtered': {'query': self.f._get_elastic_query(doc)}}}
            self.req.args = {'source': json.dumps(query)}
            docs = get_resource_service('archive').get(req=self.req, lookup=None)
            doc_ids = [d['_id'] for d in docs]
            self.assertEqual(1, docs.count())
            self.assertTrue('3' in doc_ids)

    def test_build_elastic_query_using_like_filter_multi_content_filter3(self):
        doc = {'content_filter': [{"expression": {"pf": [2]}}, {"expression": {"pf": [1], "fc": [2]}}], 'name': 'pf-1'}
        with self.app.app_context():
            query = {'query': {'filtered': {'query': self.f._get_elastic_query(doc)}}}
            self.req.args = {'source': json.dumps(query)}
            docs = get_resource_service('archive').get(req=self.req, lookup=None)
            doc_ids = [d['_id'] for d in docs]
            self.assertEqual(1, docs.count())
            self.assertTrue('3' in doc_ids)

    def test_build_elastic_query_using_like_filter_multi_content_filter4(self):
        doc = {'content_filter': [{"expression": {"pf": [2]}}, {"expression": {"pf": [3]}}], 'name': 'pf-1'}
        with self.app.app_context():
            query = {'query': {'filtered': {'query': self.f._get_elastic_query(doc)}}}
            self.req.args = {'source': json.dumps(query)}
            docs = get_resource_service('archive').get(req=self.req, lookup=None)
            doc_ids = [d['_id'] for d in docs]
            self.assertEqual(1, docs.count())
            self.assertTrue('3' in doc_ids)

    def test_build_elastic_query_using_like_filter_multi_content_filter5(self):
        doc = {'content_filter': [{"expression": {"pf": [4], "fc": [4]}}], 'name': 'pf-1'}
        with self.app.app_context():
            query = {'query': {'filtered': {'query': self.f._get_elastic_query(doc)}}}
            self.req.args = {'source': json.dumps(query)}
            docs = get_resource_service('archive').get(req=self.req, lookup=None)
            doc_ids = [d['_id'] for d in docs]
            self.assertEqual(1, docs.count())
            self.assertTrue('3' in doc_ids)


class FilteringDataTests(ContentFilterTests):

    def test_does_match_returns_true_for_nonexisting_filter(self):
        for article in self.articles:
            self.assertTrue(self.f.does_match(None, article))

    def test_does_match_custom_vocabularies(self):
        doc1 = {'content_filter': [{"expression": {"fc": [6]}}], 'name': 'mv-1'}
        doc2 = {'content_filter': [{"expression": {"fc": [7]}}], 'name': 'ct-1'}
        with self.app.app_context():
            self.assertTrue(self.f.does_match(doc1, self.articles[6]))
            self.assertTrue(self.f.does_match(doc2, self.articles[7]))

    def test_does_match_using_like_filter_single_fc(self):
        doc = {'content_filter': [{"expression": {"fc": [1]}}], 'name': 'pf-1'}
        with self.app.app_context():
            self.assertTrue(self.f.does_match(doc, self.articles[0]))
            self.assertTrue(self.f.does_match(doc, self.articles[1]))
            self.assertTrue(self.f.does_match(doc, self.articles[2]))
            self.assertFalse(self.f.does_match(doc, self.articles[3]))
            self.assertFalse(self.f.does_match(doc, self.articles[4]))
            self.assertFalse(self.f.does_match(doc, self.articles[5]))

    def test_does_match_using_like_filter_single_pf(self):
        doc = {'content_filter': [{"expression": {"pf": [1]}}], 'name': 'pf-1'}
        with self.app.app_context():
            self.assertTrue(self.f.does_match(doc, self.articles[0]))
            self.assertTrue(self.f.does_match(doc, self.articles[1]))
            self.assertTrue(self.f.does_match(doc, self.articles[2]))
            self.assertFalse(self.f.does_match(doc, self.articles[3]))
            self.assertFalse(self.f.does_match(doc, self.articles[4]))
            self.assertFalse(self.f.does_match(doc, self.articles[5]))

    def test_does_match_using_like_filter_multi_fc(self):
        doc = {'content_filter': [{"expression": {"fc": [1]}}, {"expression": {"fc": [2]}}], 'name': 'pf-1'}
        with self.app.app_context():
            self.assertTrue(self.f.does_match(doc, self.articles[0]))
            self.assertTrue(self.f.does_match(doc, self.articles[1]))
            self.assertTrue(self.f.does_match(doc, self.articles[2]))
            self.assertFalse(self.f.does_match(doc, self.articles[3]))
            self.assertTrue(self.f.does_match(doc, self.articles[4]))
            self.assertFalse(self.f.does_match(doc, self.articles[5]))

    def test_does_match_using_like_filter_multi_pf(self):
        doc = {'content_filter': [{"expression": {"pf": [1]}}, {"expression": {"fc": [2]}}], 'name': 'pf-1'}
        with self.app.app_context():
            self.assertTrue(self.f.does_match(doc, self.articles[0]))
            self.assertTrue(self.f.does_match(doc, self.articles[1]))
            self.assertTrue(self.f.does_match(doc, self.articles[2]))
            self.assertFalse(self.f.does_match(doc, self.articles[3]))
            self.assertTrue(self.f.does_match(doc, self.articles[4]))
            self.assertFalse(self.f.does_match(doc, self.articles[5]))

    def test_does_match_using_like_filter_multi_fc2(self):
        doc = {'content_filter': [{"expression": {"fc": [3, 4]}}], 'name': 'pf-1'}
        with self.app.app_context():
            self.assertFalse(self.f.does_match(doc, self.articles[0]))
            self.assertFalse(self.f.does_match(doc, self.articles[1]))
            self.assertTrue(self.f.does_match(doc, self.articles[2]))
            self.assertFalse(self.f.does_match(doc, self.articles[3]))
            self.assertFalse(self.f.does_match(doc, self.articles[4]))
            self.assertFalse(self.f.does_match(doc, self.articles[5]))

    def test_does_match_using_like_filter_multi_pf2(self):
        doc = {'content_filter': [{"expression": {"pf": [2]}}], 'name': 'pf-1'}
        with self.app.app_context():
            self.assertFalse(self.f.does_match(doc, self.articles[0]))
            self.assertFalse(self.f.does_match(doc, self.articles[1]))
            self.assertTrue(self.f.does_match(doc, self.articles[2]))
            self.assertFalse(self.f.does_match(doc, self.articles[3]))
            self.assertFalse(self.f.does_match(doc, self.articles[4]))
            self.assertFalse(self.f.does_match(doc, self.articles[5]))

    def test_does_match_using_like_filter_multi_fc3(self):
        doc = {'content_filter': [{"expression": {"fc": [3, 4]}}, {"expression": {"fc": [1, 2]}}], 'name': 'pf-1'}
        with self.app.app_context():
            self.assertFalse(self.f.does_match(doc, self.articles[0]))
            self.assertFalse(self.f.does_match(doc, self.articles[1]))
            self.assertTrue(self.f.does_match(doc, self.articles[2]))
            self.assertFalse(self.f.does_match(doc, self.articles[3]))
            self.assertFalse(self.f.does_match(doc, self.articles[4]))
            self.assertFalse(self.f.does_match(doc, self.articles[5]))

    def test_does_match_using_like_filter_multi_pf3(self):
        doc = {'content_filter': [{"expression": {"pf": [4], "fc": [4]}}], 'name': 'pf-1'}
        with self.app.app_context():
            self.assertFalse(self.f.does_match(doc, self.articles[0]))
            self.assertFalse(self.f.does_match(doc, self.articles[1]))
            self.assertTrue(self.f.does_match(doc, self.articles[2]))
            self.assertFalse(self.f.does_match(doc, self.articles[3]))
            self.assertFalse(self.f.does_match(doc, self.articles[4]))
            self.assertFalse(self.f.does_match(doc, self.articles[5]))

    def test_if_pf_is_used(self):
        with self.app.app_context():
            self.assertTrue(self.f._get_content_filters_by_content_filter(1).count() == 1)
            self.assertTrue(self.f._get_content_filters_by_content_filter(4).count() == 0)

    def test_if_fc_is_used(self):
        with self.app.app_context():
            self.assertTrue(len(self.f.get_content_filters_by_filter_condition(1)) == 2)
            self.assertTrue(len(self.f.get_content_filters_by_filter_condition(3)) == 2)
            self.assertTrue(len(self.f.get_content_filters_by_filter_condition(2)) == 1)

    def test_get_subscribers_by_filter_condition(self):
        filter_condition1 = {'field': 'urgency', 'operator': 'in', 'value': '2'}
        filter_condition2 = {'field': 'urgency', 'operator': 'in', 'value': '1'}
        filter_condition3 = {'field': 'headline', 'operator': 'like', 'value': 'tor'}
        filter_condition4 = {'field': 'urgency', 'operator': 'nin', 'value': '3'}

        with self.app.app_context():
            cmd = AppPopulateCommand()
            filename = os.path.join(os.path.abspath(
                os.path.dirname("apps/prepopulate/data_init/vocabularies.json")), "vocabularies.json")
            cmd.run(filename)
            r1 = self.s._get_subscribers_by_filter_condition(filter_condition1)
            r2 = self.s._get_subscribers_by_filter_condition(filter_condition2)
            r3 = self.s._get_subscribers_by_filter_condition(filter_condition3)
            r4 = self.s._get_subscribers_by_filter_condition(filter_condition4)
            self.assertTrue(len(r1[0]['selected_subscribers']) == 1)
            self.assertTrue(len(r2[0]['selected_subscribers']) == 0)
            self.assertTrue(len(r3[0]['selected_subscribers']) == 2)
            self.assertTrue(len(r4[0]['selected_subscribers']) == 1)


class DeleteMethodTestCase(ContentFilterTests):
    """Tests for the delete() method."""

    def test_raises_error_if_filter_referenced_by_subscribers(self):
        with self.assertRaises(SuperdeskApiError) as ctx:
            self.f.delete({'_id': 1})

        self.assertEqual(ctx.exception.status_code, 400)  # bad request error

    def test_raises_error_if_filter_referenced_by_routing_rules(self):
        with self.assertRaises(SuperdeskApiError) as ctx:
            self.f.delete({'_id': 4})

        self.assertEqual(ctx.exception.status_code, 400)  # bad request error
