# -*- coding: utf-8 -*-

from datetime import datetime
from unittest import TestCase

from mock import patch, Mock
from werkzeug.exceptions import NotFound

from distribution import app
from distribution.models.feed import FeedItem
from distribution.models import ValidationError
from distribution.utils import string_to_utc_string
from .. import utils

FIXED_DATETIME_UTC_NOW = datetime.utcnow()


class MockDatetime(datetime):
    @classmethod
    def utcnow(cls):
        return FIXED_DATETIME_UTC_NOW


class FeedItemTestCase(TestCase):
    def setUp(self):
        pass

    @patch('distribution.models.feed.TaxonomyTopic.manager.get', return_value=None)
    @patch('distribution.models.feed.FeedItem.manager.search_by_field', side_effect=NotFound)
    def test_get_key(self, search_by_field_mock, get_mock):
        """
        Test: Should return a custom key
        """
        data = {
            'id': 'news:subjects?1',
            'uri': 'test',
            'content_type': 'story',
            'title': 'Test get key',
            'publication_datetime': "Mon Mar 02 2015 11:55:00 GMT-0500",
            'last_modified_datetime': "Mon Mar 02 2015 11:55:00 GMT-0500"
        }

        feed_item = FeedItem(data, 'news')
        self.assertEqual(feed_item.key, 'news:subjects?1')

    @patch('distribution.models.feed.datetime', new=MockDatetime)
    @patch('distribution.models.feed.TaxonomyTopic.manager.get', return_value=None)
    @patch('distribution.models.feed.FeedItem.manager.search_by_field', side_effect=NotFound)
    def test_init_data(self, search_by_field_mock, get_mock):
        """
        Test: Should set default properties for data when instanced
        """
        data = utils.get_consumer_post_data()
        data = data['items'][0]
        feed_item = FeedItem(data)
        self.assertEqual(feed_item.data['publication_datetime'],
                         string_to_utc_string(data['publication_datetime']))
        self.assertEqual(feed_item.data['meta']['created_at'],
                         FIXED_DATETIME_UTC_NOW.isoformat() + "Z")
        self.assertEqual(feed_item.data['meta']['last_update'],
                         FIXED_DATETIME_UTC_NOW.isoformat() + "Z")
        self.assertIsNone(feed_item.data['meta']['domain'])

    @patch('distribution.models.feed.TaxonomyTopic.manager.get', return_value=None)
    @patch('distribution.models.feed.FeedItem.manager.search_by_field', side_effect=NotFound)
    def test_init_data_source(self, search_by_field_mock, get_mock):
        """
        Test: Should set project when it's provided
        """
        data = utils.get_consumer_post_data()
        data = data['items'][0]
        feed_item = FeedItem(data, 'awesome-project')
        self.assertEqual(feed_item.data['meta']['domain'], 'awesome-project')

    @patch('distribution.models.feed.TaxonomyTopic.manager.get', return_value=None)
    @patch('distribution.models.feed.FeedItem.manager.search_by_field', side_effect=NotFound)
    def test_init_data_source_order(self, search_by_field_mock, get_mock):
        """
        Test: Should keep the subjects order
        """
        data = utils.get_consumer_post_data()
        data = data['items'][0]
        data_subjects = data['subjects']
        feed_item = FeedItem(data, 'awesome-project')
        self.assertEqual(feed_item.data['subjects'], data_subjects)

    @patch('distribution.models.feed.TaxonomyTopic.manager.get', return_value=None)
    @patch('distribution.models.feed.FeedItem.manager.get_by_field')
    @patch('distribution.models.feed.FeedItem.manager.search_by_field')
    def test_feed_item_not_root_localization(self, search_by_field_mock, get_by_field_mock,
                                             get_mock):
        """
        Test: get all localizations from a not root element
        """

        def search_by_field_mock_side_effect(*args):
            return utils.get_stored_element_children(data, args[1])

        def get_by_field_mock_side_effect(*args):
            return utils.get_stored_item_by_id(data, args[0], args[1])

        # Test setup
        data = utils.get_localization_mock_data()
        search_by_field_mock.side_effect = search_by_field_mock_side_effect
        get_by_field_mock.side_effect = get_by_field_mock_side_effect

        # Test execution
        feed_item = FeedItem(data[4], 'awesome-project')
        feed_item_localizations = feed_item.localizations
        self.assertEqual(len(feed_item_localizations), 5)
        for element in feed_item_localizations:
            self.assertNotEqual(element['id'], data[4]['id'])

    @patch('distribution.models.feed.TaxonomyTopic.manager.get', return_value=None)
    @patch('distribution.models.feed.FeedItem.manager.get_by_field')
    @patch('distribution.models.feed.FeedItem.manager.search_by_field')
    def test_feed_item_root_localization(self, search_by_field_mock, get_by_field_mock,
                                         get_mock):
        """
        Test: get all localizations from the root element
        """

        def search_by_field_mock_side_effect(*args):
            return utils.get_stored_element_children(data, args[1])

        def get_by_field_mock_side_effect(*args):
            return utils.get_stored_item_by_id(data, args[0], args[1])

        # Test setup
        data = utils.get_localization_mock_data()
        search_by_field_mock.side_effect = search_by_field_mock_side_effect
        get_by_field_mock.side_effect = get_by_field_mock_side_effect

        # Test execution
        feed_item = FeedItem(data[0], 'awesome-project')
        feed_item_localizations = feed_item.localizations
        self.assertEqual(len(feed_item_localizations), 5)
        for element in feed_item_localizations:
            self.assertNotEqual(element['id'], data[0]['id'])

    @patch('distribution.models.feed.TaxonomyTopic.manager.get', return_value=None)
    @patch('distribution.models.feed.FeedItem.manager.get_by_field')
    @patch('distribution.models.feed.FeedItem.manager.search_by_field')
    def test_feed_item_root_localization_infinite_loop(
            self, search_by_field_mock, get_by_field_mock, get_mock):
        """Test raise ValidationError if an infinite loop occurs
        between Feed Items (if an article has as parent its child).
        """

        def search_by_field_mock_side_effect(*args):
            return utils.get_stored_element_children(data, args[1])

        def get_by_field_mock_side_effect(*args):
            return utils.get_stored_item_by_id(data, args[0], args[1], False)

        # Test setup
        data = utils.get_json('infinite_loop_data.json')

        search_by_field_mock.side_effect = search_by_field_mock_side_effect
        get_by_field_mock.side_effect = get_by_field_mock_side_effect

        # Test execution
        with self.assertRaises(ValidationError):
            FeedItem(data[0], 'awesome-project')

    @patch('distribution.models.feed.TaxonomyTopic.manager.search_by_name_and_type')
    @patch('distribution.models.feed.TaxonomyTopic.manager.get', return_value=None)
    def test_feed_item_unknown_terms(self, get_mock, search_by_name_and_type_mock):
        """Test that unknown_terms key is well formed when a FeedItem is created."""

        def search_by_name_and_type_mock_side_effect(name, topic_type):
            topic = Mock()
            if topic_type is None:
                topic_type = 'keywords'
            topic.data = {
                'topic_type': topic_type,
                'uuid': name
            }
            return [topic]

        data = {
            'id': 'news:subjects?1',
            'uri': 'test',
            'content_type': 'story',
            'title': 'Test get key',
            'publication_datetime': "Mon Mar 02 2015 11:55:00 GMT-0500",
            'last_modified_datetime': "Mon Mar 02 2015 11:55:00 GMT-0500",
            'unknown_terms': {
                '': [
                    'unnamed1',
                    'unnamed2'
                ]
            },
        }

        for topic_type in app.config['TAXONOMY_TOPIC_TYPES']:
            data['unknown_terms'][topic_type] = [
                '{}.name'.format(topic_type)
            ]

        search_by_name_and_type_mock.side_effect = \
            search_by_name_and_type_mock_side_effect

        feed_item = FeedItem(data, 'awesome-project')
        unknown_terms = data['unknown_terms']

        for key, value in unknown_terms.iteritems():
            if key == '':
                key = 'keywords'
            self.assertEqual(value, feed_item.data[key])
