from datetime import datetime, timezone
from unittest.mock import Mock

from django.test import TestCase

from freezegun import freeze_time

from newsreader.news.collection.feed import FeedBuilder
from newsreader.news.collection.tests.factories import FeedFactory
from newsreader.news.core.models import Post
from newsreader.news.core.tests.factories import FeedPostFactory

from .mocks import (
    mock_with_html,
    mock_with_long_author,
    mock_with_long_exotic_title,
    mock_with_long_title,
    mock_with_longer_content_detail,
    mock_with_multiple_content_detail,
    mock_with_shorter_content_detail,
    mock_with_update_entries,
    mock_without_author,
    mock_without_body,
    mock_without_entries,
    mock_without_identifier,
    mock_without_publish_date,
    mock_without_url,
    multiple_mock,
)


@freeze_time("2019-10-30 12:30:00")
class FeedBuilderTestCase(TestCase):
    def setUp(self):
        self.maxDiff = None

    def test_multiple_entries(self):
        rule = FeedFactory()
        mock_stream = Mock(rule=rule)

        with FeedBuilder(multiple_mock, mock_stream) as builder:
            builder.build()
            builder.save()

        posts = Post.objects.order_by("-publication_date")
        self.assertEqual(Post.objects.count(), 3)

        post = posts[0]

        publication_date = datetime(
            2019, 5, 20, hour=16, minute=32, second=38, tzinfo=timezone.utc
        )

        self.assertEqual(
            post.publication_date.strftime("%Y-%m-%d %H:%M:%S"),
            publication_date.strftime("%Y-%m-%d %H:%M:%S"),
        )

        self.assertEqual(
            post.remote_identifier,
            "https://www.bbc.co.uk/news/uk-england-birmingham-48339080",
        )

        self.assertEqual(
            post.url, "https://www.bbc.co.uk/news/uk-england-birmingham-48339080"
        )

        self.assertEqual(
            post.title, "Birmingham head teacher threatened over LGBT lessons"
        )

        post = posts[1]

        publication_date = datetime(
            2019, 5, 20, hour=16, minute=7, second=37, tzinfo=timezone.utc
        )

        self.assertEqual(
            post.publication_date.strftime("%Y-%m-%d %H:%M:%S"),
            publication_date.strftime("%Y-%m-%d %H:%M:%S"),
        )

        self.assertEqual(
            post.remote_identifier,
            "https://www.bbc.co.uk/news/world-us-canada-48338168",
        )

        self.assertEqual(
            post.url, "https://www.bbc.co.uk/news/world-us-canada-48338168"
        )

        self.assertEqual(
            post.title, "Trump's 'genocidal taunts' will not end Iran - Zarif"
        )

    def test_entries_without_remote_identifier(self):
        rule = FeedFactory()
        mock_stream = Mock(rule=rule)

        with FeedBuilder(mock_without_identifier, mock_stream) as builder:
            builder.build()
            builder.save()

        posts = Post.objects.order_by("-publication_date")
        self.assertEqual(Post.objects.count(), 2)

        post = posts[0]

        publication_date = datetime(
            2019, 5, 20, hour=16, minute=7, second=37, tzinfo=timezone.utc
        )

        self.assertEqual(post.publication_date, publication_date)
        self.assertEqual(post.remote_identifier, None)
        self.assertEqual(
            post.url, "https://www.bbc.co.uk/news/world-us-canada-48338168"
        )
        self.assertEqual(
            post.title, "Trump's 'genocidal taunts' will not end Iran - Zarif"
        )

        post = posts[1]

        publication_date = datetime(
            2019, 5, 20, hour=12, minute=19, second=19, tzinfo=timezone.utc
        )

        self.assertEqual(post.publication_date, publication_date)
        self.assertEqual(post.remote_identifier, None)
        self.assertEqual(post.url, "https://www.bbc.co.uk/news/technology-48334739")
        self.assertEqual(post.title, "Huawei's Android loss: How it affects you")

    def test_entry_without_publication_date(self):
        rule = FeedFactory()
        mock_stream = Mock(rule=rule)

        with FeedBuilder(mock_without_publish_date, mock_stream) as builder:
            builder.build()
            builder.save()

        posts = Post.objects.order_by("-publication_date")
        self.assertEqual(Post.objects.count(), 2)

        post = posts[0]

        self.assertEqual(
            post.publication_date.strftime("%Y-%m-%d %H:%M"), "2019-10-30 12:30"
        )
        self.assertEqual(post.created, datetime.now(tz=timezone.utc))
        self.assertEqual(
            post.remote_identifier,
            "https://www.bbc.co.uk/news/world-us-canada-48338168",
        )

        post = posts[1]

        self.assertEqual(
            post.publication_date.strftime("%Y-%m-%d %H:%M"), "2019-10-30 12:30"
        )
        self.assertEqual(post.created, datetime.now(tz=timezone.utc))
        self.assertEqual(
            post.remote_identifier, "https://www.bbc.co.uk/news/technology-48334739"
        )

    def test_entry_without_url(self):
        rule = FeedFactory()
        mock_stream = Mock(rule=rule)

        with FeedBuilder(mock_without_url, mock_stream) as builder:
            builder.build()
            builder.save()

        posts = Post.objects.order_by("-publication_date")
        self.assertEqual(Post.objects.count(), 2)

        post = posts[0]

        self.assertEqual(post.created, datetime.now(tz=timezone.utc))
        self.assertEqual(
            post.remote_identifier,
            "https://www.bbc.co.uk/news/world-us-canada-48338168",
        )

        post = posts[1]

        self.assertEqual(post.created, datetime.now(tz=timezone.utc))
        self.assertEqual(
            post.remote_identifier, "https://www.bbc.co.uk/news/technology-48334739"
        )

    def test_entry_without_body(self):
        rule = FeedFactory()
        mock_stream = Mock(rule=rule)

        with FeedBuilder(mock_without_body, mock_stream) as builder:
            builder.build()
            builder.save()

        posts = Post.objects.order_by("-publication_date")

        self.assertEqual(Post.objects.count(), 2)

        post = posts[0]

        self.assertEqual(
            post.created.strftime("%Y-%m-%d %H:%M:%S"), "2019-10-30 12:30:00"
        )
        self.assertEqual(
            post.remote_identifier,
            "https://www.bbc.co.uk/news/uk-england-birmingham-48339080",
        )
        self.assertEqual(post.body, "")

        post = posts[1]

        self.assertEqual(
            post.created.strftime("%Y-%m-%d %H:%M:%S"), "2019-10-30 12:30:00"
        )
        self.assertEqual(
            post.remote_identifier,
            "https://www.bbc.co.uk/news/world-us-canada-48338168",
        )
        self.assertEqual(post.body, "")

    def test_entry_without_author(self):
        rule = FeedFactory()
        mock_stream = Mock(rule=rule)

        with FeedBuilder(mock_without_author, mock_stream) as builder:
            builder.build()
            builder.save()

        posts = Post.objects.order_by("-publication_date")
        self.assertEqual(Post.objects.count(), 2)

        post = posts[0]

        self.assertEqual(post.created, datetime.now(tz=timezone.utc))
        self.assertEqual(
            post.remote_identifier,
            "https://www.bbc.co.uk/news/world-us-canada-48338168",
        )
        self.assertEqual(post.author, None)

        post = posts[1]

        self.assertEqual(post.created, datetime.now(tz=timezone.utc))
        self.assertEqual(
            post.remote_identifier, "https://www.bbc.co.uk/news/technology-48334739"
        )
        self.assertEqual(post.author, None)

    def test_empty_entries(self):
        rule = FeedFactory()
        mock_stream = Mock(rule=rule)

        with FeedBuilder(mock_without_entries, mock_stream) as builder:
            builder.build()
            builder.save()

        self.assertEqual(Post.objects.count(), 0)

    def test_update_entries(self):
        rule = FeedFactory()
        mock_stream = Mock(rule=rule)

        existing_first_post = FeedPostFactory.create(
            remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7", rule=rule
        )

        existing_second_post = FeedPostFactory.create(
            remote_identifier="a5479c66-8fae-11e9-8422-00163ef6bee7", rule=rule
        )

        with FeedBuilder(mock_with_update_entries, mock_stream) as builder:
            builder.build()
            builder.save()

        self.assertEqual(Post.objects.count(), 3)

        existing_first_post.refresh_from_db()
        existing_second_post.refresh_from_db()

        self.assertEqual(
            existing_first_post.title,
            "Trump's 'genocidal taunts' will not end Iran - Zarif",
        )

        self.assertEqual(
            existing_second_post.title, "Huawei's Android loss: How it affects you"
        )

    def test_html_sanitizing(self):
        rule = FeedFactory()
        mock_stream = Mock(rule=rule)

        with FeedBuilder(mock_with_html, mock_stream) as builder:
            builder.build()
            builder.save()

        post = Post.objects.get()

        self.assertEqual(Post.objects.count(), 1)

        self.assertTrue("<article>" in post.body)
        self.assertTrue("<h1>" in post.body)
        self.assertTrue("<strong>" in post.body)
        self.assertTrue('<a href="https://www.bbc.com">' in post.body)
        self.assertTrue("<p>" in post.body)

        self.assertTrue("<html>" not in post.body)
        self.assertTrue("<script>" not in post.body)
        self.assertTrue("<iframe>" not in post.body)

    def test_long_author_text_is_truncated(self):
        rule = FeedFactory()
        mock_stream = Mock(rule=rule)

        with FeedBuilder(mock_with_long_author, mock_stream) as builder:
            builder.build()
            builder.save()

        post = Post.objects.get()

        self.assertEqual(Post.objects.count(), 1)

        self.assertEqual(len(post.author), 40)

    def test_long_title_text_is_truncated(self):
        rule = FeedFactory()
        mock_stream = Mock(rule=rule)

        with FeedBuilder(mock_with_long_title, mock_stream) as builder:
            builder.build()
            builder.save()

        post = Post.objects.get()

        self.assertEqual(Post.objects.count(), 1)

        self.assertEqual(len(post.title), 200)
        self.assertTrue(post.title.endswith("…"))

    def test_long_title_exotic_title(self):
        rule = FeedFactory()
        mock_stream = Mock(rule=rule)

        with FeedBuilder(mock_with_long_exotic_title, mock_stream) as builder:
            builder.build()
            builder.save()

        post = Post.objects.get()

        self.assertEqual(Post.objects.count(), 1)

        self.assertEqual(len(post.title), 200)
        self.assertTrue(post.title.endswith("…"))

    def test_content_detail_is_prioritized_if_longer(self):
        rule = FeedFactory()
        mock_stream = Mock(rule=rule)

        with FeedBuilder(mock_with_longer_content_detail, mock_stream) as builder:
            builder.build()
            builder.save()

        post = Post.objects.get()

        self.assertEqual(Post.objects.count(), 1)

        self.assertFalse(
            "Foreign Minister Mohammad Javad Zarif says the US" in post.body
        )
        self.assertTrue("Federal Communications Commission" in post.body)

    def test_content_detail_is_not_prioritized_if_shorter(self):
        rule = FeedFactory()
        mock_stream = Mock(rule=rule)

        with FeedBuilder(mock_with_shorter_content_detail, mock_stream) as builder:
            builder.build()
            builder.save()

        post = Post.objects.get()

        self.assertEqual(Post.objects.count(), 1)

        self.assertTrue(
            "Foreign Minister Mohammad Javad Zarif says the US" in post.body
        )

    def test_content_detail_is_concatinated(self):
        rule = FeedFactory()
        mock_stream = Mock(rule=rule)

        with FeedBuilder(mock_with_multiple_content_detail, mock_stream) as builder:
            builder.build()
            builder.save()

        post = Post.objects.get()

        self.assertEqual(Post.objects.count(), 1)

        self.assertEqual(post.body, "Yippie\n Ya\n Yee")
