# -*- coding: utf-8 -*-
import os

from mock import call, MagicMock, patch, PropertyMock
from unittest import TestCase

from distribution import app
from distribution import settings
from distribution.adapters.clients import amp as amp_client
from distribution.adapters.models import AMPAdapter
from distribution.adapters.rules import (
    AMPRuleManager,
    ArticleReferenceAMPRule,
    ArticleStoryAMPRule
)


class AMPClientTestCase(TestCase):
    """
    Test interaction with the Google AMP CDN cache.
    """

    def test_get_amp_cache_url(self):
        """
        Build AMP cache URL
        """
        url = 'http://unittest.natgeo.com/my/cool/example/'
        test_url = '{}c/{{}}{}/public/amp/my/cool/example/'.format(
            app.config['AMP_CACHE_URL'],
            app.config['AMP_PUBLIC_HOST'])

        with app.test_request_context():
            cache_url = amp_client.get_amp_cache_url(url)
            self.assertEqual(test_url.format(''), cache_url)
            url = url.replace('http://', 'https://')
            cache_url = amp_client.get_amp_cache_url(url)
            self.assertEqual(test_url.format('s/'), cache_url)

    @patch('distribution.adapters.clients.amp.app.logger.error')
    @patch('distribution.adapters.clients.amp.requests.get')
    @patch('distribution.adapters.clients.amp.requests.head')
    @patch('distribution.adapters.clients.amp.get_amp_cache_url')
    def test_get_update_ping_successfully(self, mock_get_amp_cache_url, mock_head_request,
                                          mock_get_request, mock_logger):
        """
        The response from the update-ping endpoing returned 204
        """
        amp_url = 'http://fake.com/public/amp/my/cool/example/'
        mock_get_amp_cache_url.return_value = amp_url
        mock_head_request.return_value.status_code = 200
        mock_get_request.return_value.status_code = 204
        amp_client.update_cache('i_dont_care')
        mock_head_request.assert_called_once_with(amp_url, verify=False)
        mock_get_request.assert_called_once_with(amp_url, verify=False)
        mock_logger.assert_not_called()

    @patch('distribution.adapters.clients.amp.app.logger.error')
    @patch('distribution.adapters.clients.amp.requests.get')
    @patch('distribution.adapters.clients.amp.requests.head')
    @patch('distribution.adapters.clients.amp.get_amp_cache_url')
    def test_get_update_ping_error(self, mock_get_amp_cache_url, mock_head_request,
                                   mock_get_request, mock_logger):
        """
        The response from the update-ping endpoing didn't returned 204
        """
        amp_url = 'http://fake.com/public/amp/my/cool/example/'
        mock_get_amp_cache_url.return_value = amp_url
        mock_head_request.return_value.status_code = 200
        mock_get_request.return_value.status_code = 500
        amp_client.update_cache('i_dont_care')
        mock_head_request.assert_called_once_with(amp_url, verify=False)
        mock_get_request.assert_called_once_with(amp_url, verify=False)
        mock_logger.assert_called_once()

    @patch('distribution.adapters.clients.amp.app.logger.error')
    @patch('distribution.adapters.clients.amp.requests.get')
    @patch('distribution.adapters.clients.amp.requests.head')
    @patch('distribution.adapters.clients.amp.get_amp_cache_url')
    def test_article_not_live(self, mock_get_amp_cache_url, mock_head_request,
                              mock_get_request, mock_logger):
        """
        The given URL is not live in AMP_PUBLIC_HOST
        """
        amp_url = 'http://fake.com/public/amp/my/cool/example/'
        mock_get_amp_cache_url.return_value = amp_url
        mock_head_request.return_value.status_code = 404
        amp_client.update_cache('i_dont_care')
        mock_head_request.assert_called_once_with(amp_url, verify=False)
        mock_get_request.assert_not_called()
        mock_logger.assert_not_called()


class AMPAdapterTestCase(TestCase):

    @patch.object(AMPAdapter, 'output_folder', return_value='/test', new_callable=PropertyMock)
    @patch('distribution.adapters.models.AMPAdapter.pre_render')
    @patch('distribution.models.feed.FeedItemManager.mget', return_value=['foo', 'bar'])
    @patch('distribution.adapters.models.os.remove')
    @patch('distribution.adapters.models.AMPAdapter.get_amp_version_from_npm', return_value='1.3.0')
    def test_files_removed(self, amp_version_mock, remove_mock, mget_mock,
                           pre_render_mock, output_mock):
        os.listdir = MagicMock(return_value=['1.2.1_foo.html', '1.2.1_bar.html', '1.3.0_wow.html'])
        adapter = AMPAdapter()
        adapter.update_amp_files()
        self.assertTrue(amp_version_mock.called)
        calls = [call(os.path.join('/test', '1.2.1_bar.html')),
                 call(os.path.join('/test', '1.2.1_foo.html'))]
        remove_mock.assert_has_calls(calls, any_order=True)
        mget_mock.assert_called_once_with(['foo', 'bar'])
        output_mock.assert_called()
        # check that the output_mock is called once per file in os.listdir
        self.assertEquals(output_mock.call_count, 3)
        # check that the remove_mock is called only to the files to remove
        self.assertEquals(remove_mock.call_count, 2)
        pre_render_mock.assert_called_once_with(['foo', 'bar'], background=False)

    def test_get_amp_rule(self):
        rule_manager = AMPRuleManager()

        rule = rule_manager.get_amp_rule('article:story')
        self.assertIsInstance(rule, ArticleStoryAMPRule)

        rule = rule_manager.get_amp_rule('article:reference')
        self.assertIsInstance(rule, ArticleReferenceAMPRule)

        rule = rule_manager.get_amp_rule('whatever')
        self.assertIsNone(rule)

    def test_amp_article_story_correct_source_and_domain_rule(self):
        """Correct article story will be rendered by AMPAdapter."""
        for domain in ArticleStoryAMPRule.allowed_sources:
            sources = [settings.SOURCES_MAP[domain]]

            rule = ArticleStoryAMPRule()

            self.assertTrue(rule.validate(domain, sources, []))

    def test_amp_article_story_correct_genre_and_domain_rule(self):
        """Correct article story will be rendered by AMPAdapter."""
        for domain in ArticleStoryAMPRule.allowed_genres:
            genres = [settings.GENRES_MAP[domain]]

            rule = ArticleStoryAMPRule()

            self.assertTrue(rule.validate(domain, [], genres))

    def test_amp_article_story_incorrect_domain_rule(self):
        """Incorrect domain for article story won't be rendered by AMPAdapter."""
        domain = 'gs:es'
        sources = [settings.SOURCES_MAP[settings.NEWS]]

        rule = ArticleStoryAMPRule()

        self.assertFalse(rule.validate(domain, sources, []))

    def test_amp_article_story_incorrect_source_rule(self):
        """Incorrect article story won't be rendered by AMPAdapter."""
        sources = [settings.SOURCES_MAP[settings.ANIMALS]]

        rule = ArticleReferenceAMPRule()

        self.assertFalse(rule.validate(settings.ANIMALS, sources, []))

    def test_amp_article_reference_correct_rule(self):
        """Correct article reference will be rendered by AMPAdapter."""
        sources = [settings.SOURCES_MAP[
            ArticleReferenceAMPRule.allowed_sources[0]]
        ]
        genres = [settings.GENRES_MAP[
            ArticleReferenceAMPRule.allowed_genres[0]]
        ]

        rule = ArticleReferenceAMPRule()

        self.assertTrue(rule.validate(settings.ANIMALS, sources, genres))

    def test_amp_article_reference_incorrect_domain_rule(self):
        """Incorrect domain for article reference won't be rendered by AMPAdapter."""
        domain = 'gs:uk'

        sources = [settings.SOURCES_MAP[
            ArticleReferenceAMPRule.allowed_sources[0]]
        ]
        genres = [settings.GENRES_MAP[
            ArticleReferenceAMPRule.allowed_genres[0]]
        ]

        rule = ArticleReferenceAMPRule()

        self.assertFalse(rule.validate(domain, sources, genres))

    def test_amp_article_reference_incorrect_source_and_genre_rule(self):
        """Incorrect article reference won't be rendered by AMPAdapter."""
        sources = [settings.SOURCES_MAP[settings.NEWS]]

        rule = ArticleReferenceAMPRule()

        self.assertFalse(rule.validate(settings.NEWS, sources, []))
