# -*- coding: utf-8 -*-
from __future__ import division

import math
from xml.etree import ElementTree

from dateutil.parser import parse
from mock import patch, Mock

from distribution import app
from distribution.models.taxonomy import TaxonomyTopic
from . import ResourceTestCase
from .. import utils


@patch('elasticsearch.Elasticsearch.search')
@patch('distribution.managers.backends.elastic.ElasticBackend._init_indices', Mock())
@patch('distribution.managers.sitemap.SitemapURLManager.get_by_field')
@patch('distribution.managers.sitemap.SitemapURLManager.search_by_field')
class SitemapPageTestCase(ResourceTestCase):

    domain = 'domain3.name'
    local_domain = 'http://localhost'
    page_url = '/public/sitemap/{}/items.{}.xml'
    index_url = '/public/sitemap/{}/index.xml'
    data = None

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

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

    def search_mock_side_effect(self, *args):
        return utils.get_stored_elements_for_sitemap(
            self.data,
            args[2],
            parse('2017-10-01T12:00:00.000000Z')
        )

    def setUp(self):
        super(SitemapPageTestCase, self).setUp()
        self.data = utils.get_sitemap_localization_mock_data()

    def test_empty_page(self, search_by_field_mock, get_by_field_mock, search_mock):
        search_by_field_mock.side_effect = self.search_by_field_mock_side_effect
        get_by_field_mock.side_effect = self.get_by_field_mock_side_effect
        search_mock.side_effect = self.search_mock_side_effect

        res = self.app.get(self.page_url.format('domain2.name', 2))
        self.assertEquals((res.status_code, res.mimetype), (404, 'application/json'))

    def test_not_published_elements(self, search_by_field_mock, get_by_field_mock, search_mock):
        search_by_field_mock.side_effect = self.search_by_field_mock_side_effect
        get_by_field_mock.side_effect = self.get_by_field_mock_side_effect
        search_mock.side_effect = self.search_mock_side_effect

        res = self.app.get(self.page_url.format('domain3.name', 1))
        self.assertEquals((res.status_code, res.mimetype), (200, 'text/xml'))

        tree = ElementTree.fromstring(res.data)
        for url in tree:
            self.assertNotEqual(url[0].text, self.data[-1]['url'])

    def test_validate_response(self, search_by_field_mock, get_by_field_mock, search_mock):
        search_by_field_mock.side_effect = self.search_by_field_mock_side_effect
        get_by_field_mock.side_effect = self.get_by_field_mock_side_effect
        search_mock.side_effect = self.search_mock_side_effect

        res = self.app.get(self.page_url.format(self.domain, 1))
        self.assertEquals((res.status_code, res.mimetype), (200, 'text/xml'))

        tree = ElementTree.fromstring(res.data)

        urls = []
        for sitemap_url in self.data:
            urls.append(sitemap_url['url'])

        for url in tree:
            # Verify url was in the received
            self.assertIn(url[0].text, urls)

            # Verify localizations based on the data
            localizations = url[5:]
            for localization in localizations:
                self.assertIn(localization.attrib.get('href'), urls)

    def _compare_fields(self, tree, fields, to_validate, element_url):
        missing_priority_node = next(node for node in tree
                                     if node[0].text == element_url)
        to_validate = [fields[field] for field in xrange(len(fields)) if field in to_validate]
        for node_index in xrange(len(to_validate)):
            self.assertEqual(missing_priority_node[node_index].tag, to_validate[node_index])

    def test_validate_missing_fields(self, search_by_field_mock, get_by_field_mock, search_mock):
        search_by_field_mock.side_effect = self.search_by_field_mock_side_effect
        get_by_field_mock.side_effect = self.get_by_field_mock_side_effect
        search_mock.side_effect = self.search_mock_side_effect

        res = self.app.get(self.page_url.format(self.domain, 1))
        self.assertEquals((res.status_code, res.mimetype), (200, 'text/xml'))

        tree = ElementTree.fromstring(res.data)

        fields = [
            '{http://www.sitemaps.org/schemas/sitemap/0.9}loc',
            '{http://www.sitemaps.org/schemas/sitemap/0.9}lastmod',
            '{http://www.sitemaps.org/schemas/sitemap/0.9}priority',
            '{http://www.sitemaps.org/schemas/sitemap/0.9}changefreq',
            '{http://www.google.com/schemas/sitemap-image/1.1}image',
            '{http://www.w3.org/1999/xhtml}link'
        ]

        self._compare_fields(tree, fields, [0, 1, 3, 5], 'https://domain3.name/test2017070108')
        self._compare_fields(tree, fields, [0, 1, 2, 5], 'https://domain3.name/test2017070107')
        self._compare_fields(tree, fields, [0, 1, 2, 3, 4, 5], 'https://domain3.name/test2017070101')


@patch('elasticsearch.Elasticsearch.search')
@patch('distribution.managers.backends.elastic.ElasticBackend._init_indices', Mock())
@patch('distribution.models.feed.TaxonomyTopic.manager.mget')
class SitemapNewsTestCase(ResourceTestCase):

    domain = 'domain3.name'
    local_domain = 'http://localhost'
    news_url = '/public/sitemap/{}/news.xml'
    data = None

    def setUp(self):
        super(SitemapNewsTestCase, self).setUp()
        self.data = utils.get_sitemap_news_mock_data()

    def test_empty_page(self, taxonomy_mock, search_mock):
        taxonomy_mock.return_value = []
        search_mock.return_value = utils.wrap_search_response([])
        res = self.app.get(self.news_url.format('domain2.name'))
        taxonomy_mock.assert_not_called()
        search_mock.assert_called_once()
        self.assertEquals((res.status_code, res.mimetype), (200, 'text/xml'))

    def test_validate_response(self, taxonomy_mock, search_mock):
        search_mock.return_value = utils.wrap_search_response(self.data)
        res = self.app.get(self.news_url.format(self.domain))
        self.assertEquals((res.status_code, res.mimetype), (200, 'text/xml'))
        search_mock.assert_called_once()
        tree = ElementTree.fromstring(res.data)
        tag = '{http://www.google.com/schemas/sitemap-news/0.9}'
        publication_date = tree.find('.//{}publication_date'.format(tag))
        title = tree.find('.//{}title'.format(tag))
        keywords = tree.find('.//{}keywords'.format(tag))
        self.assertEqual(publication_date.text, '2017-08-03T20:51:51+00:00')
        self.assertEqual(title.text, self.data[0]['title'])
        self.assertEqual(keywords.text, 'business, merger, acquisition')


class SitemapIndexTestCase(ResourceTestCase):
    max_rows_temp = 1000
    domain = 'domain.name1'
    local_domain = 'http://localhost'
    page_url = '/public/sitemap/{}/items.{}.xml'
    index_url = '/public/sitemap/{}/index.xml'.format(domain)

    @classmethod
    def setUpClass(cls):
        cls.max_rows_temp = app.config['SITEMAP_MAX_ROWS_LIMIT']

    @classmethod
    def tearDownClass(cls):
        app.config['SITEMAP_MAX_ROWS_LIMIT'] = cls.max_rows_temp

    def _get(self):
        """
        Makes an anonymous GET request
        :return:
        """
        return self.app.get(self.index_url)

    @patch('distribution.models.feed.FeedItem.localizations', [])
    @patch('distribution.managers.BaseManager.count', return_value={"count": 0})
    def test_validate_no_elements_for_domain(self, count_mock):
        res = self._get()
        self.assertEqual((res.status_code, res.mimetype),
                         (404, 'application/json'))

    @patch('distribution.models.feed.FeedItem.localizations', [])
    @patch('distribution.managers.BaseManager.count', return_value={"count": 5})
    def test_validate_xml(self, count_mock):

        max_rows = 2
        app.config['SITEMAP_MAX_ROWS_LIMIT'] = max_rows

        res = self._get()
        self.assertEquals((res.status_code, res.mimetype),
                          (200, 'text/xml'))
        tree = ElementTree.fromstring(res.data)

        self.assertEqual(len(tree), math.ceil(count_mock()['count']/max_rows))

        for child in xrange(len(tree[0])):
            current_page_url = self.page_url.format(self.domain, child+1)
            self.assertEqual(
                tree[0][child].text,
                '{}{}'.format(self.local_domain, current_page_url))
