from datetime import datetime
from unittest.mock import Mock

from django.test import TestCase

import pytz

from newsreader.news.collection.reddit import RedditBuilder
from newsreader.news.collection.tests.factories import SubredditFactory
from newsreader.news.collection.tests.reddit.builder.mocks import *
from newsreader.news.core.models import Post
from newsreader.news.core.tests.factories import RedditPostFactory


class RedditBuilderTestCase(TestCase):
    def setUp(self):
        self.maxDiff = None

    def test_simple_mock(self):
        builder = RedditBuilder

        subreddit = SubredditFactory()
        mock_stream = Mock(rule=subreddit)

        with builder(simple_mock, mock_stream) as builder:
            builder.build()
            builder.save()

        posts = {post.remote_identifier: post for post in Post.objects.all()}

        self.assertCountEqual(
            ("hm0qct", "hna75r", "hngs71", "hngsj8", "hnd7cy"), posts.keys()
        )

        post = posts["hm0qct"]

        self.assertEquals(post.rule, subreddit)
        self.assertEquals(
            post.title,
            "Linux Experiences/Rants or Education/Certifications thread - July 06, 2020",
        )
        self.assertIn(
            " This megathread is also to hear opinions from anyone just starting out"
            " with Linux or those that have used Linux (GNU or otherwise) for a long",
            post.body,
        )

        self.assertIn(
            "<p>For those looking for certifications please use this megathread to ask about how"
            " to get certified whether it&#39;s for the business world or for your own satisfaction."
            ' Be sure to check out <a href="/r/linuxadmin">r/linuxadmin</a> for more discussion in the'
            " SysAdmin world!</p>",
            post.body,
        )

        self.assertEquals(post.author, "AutoModerator")
        self.assertEquals(
            post.url,
            "https://www.reddit.com/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/",
        )
        self.assertEquals(
            post.publication_date, pytz.utc.localize(datetime(2020, 7, 6, 6, 11, 22))
        )

    def test_empty_data(self):
        builder = RedditBuilder

        subreddit = SubredditFactory()
        mock_stream = Mock(rule=subreddit)

        with builder(empty_mock, mock_stream) as builder:
            builder.build()
            builder.save()

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

    def test_unknown_mock(self):
        builder = RedditBuilder

        subreddit = SubredditFactory()
        mock_stream = Mock(rule=subreddit)

        with builder(unknown_mock, mock_stream) as builder:
            builder.build()
            builder.save()

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

    def test_html_sanitizing(self):
        builder = RedditBuilder

        subreddit = SubredditFactory()
        mock_stream = Mock(rule=subreddit)

        with builder(unsanitized_mock, mock_stream) as builder:
            builder.build()
            builder.save()

        posts = {post.remote_identifier: post for post in Post.objects.all()}

        self.assertCountEqual(("hnd7cy",), posts.keys())

        post = posts["hnd7cy"]

        self.assertEquals(post.body, "<article></article>")

    def test_long_author_text_is_truncated(self):
        builder = RedditBuilder

        subreddit = SubredditFactory()
        mock_stream = Mock(rule=subreddit)

        with builder(author_mock, mock_stream) as builder:
            builder.build()
            builder.save()

        posts = {post.remote_identifier: post for post in Post.objects.all()}

        self.assertCountEqual(("hnd7cy",), posts.keys())

        post = posts["hnd7cy"]

        self.assertEquals(post.author, "TheQuantumZeroTheQuantumZeroTheQuantumZ…")

    def test_long_title_text_is_truncated(self):
        builder = RedditBuilder

        subreddit = SubredditFactory()
        mock_stream = Mock(rule=subreddit)

        with builder(title_mock, mock_stream) as builder:
            builder.build()
            builder.save()

        posts = {post.remote_identifier: post for post in Post.objects.all()}

        self.assertCountEqual(("hnd7cy",), posts.keys())

        post = posts["hnd7cy"]

        self.assertEquals(
            post.title,
            'Board statement on the LibreOffice 7.0 RC "Personal EditionBoard statement on the LibreOffice 7.0 RC "Personal Edition" label" labelBoard statement on the LibreOffice 7.0 RC "PersBoard statement on t…',
        )

    def test_duplicate_in_response(self):
        builder = RedditBuilder

        subreddit = SubredditFactory()
        mock_stream = Mock(rule=subreddit)

        with builder(duplicate_mock, mock_stream) as builder:
            builder.build()
            builder.save()

        posts = {post.remote_identifier: post for post in Post.objects.all()}

        self.assertEquals(Post.objects.count(), 2)
        self.assertCountEqual(("hm0qct", "hna75r"), posts.keys())

    def test_duplicate_in_database(self):
        builder = RedditBuilder

        subreddit = SubredditFactory()
        mock_stream = Mock(rule=subreddit)

        duplicate_post = RedditPostFactory(
            remote_identifier="hm0qct", rule=subreddit, title="foo"
        )

        with builder(simple_mock, mock_stream) as builder:
            builder.build()
            builder.save()

        posts = {post.remote_identifier: post for post in Post.objects.all()}

        self.assertEquals(Post.objects.count(), 5)
        self.assertCountEqual(
            ("hm0qct", "hna75r", "hngs71", "hngsj8", "hnd7cy"), posts.keys()
        )

    def test_image_post(self):
        builder = RedditBuilder

        subreddit = SubredditFactory()
        mock_stream = Mock(rule=subreddit)

        with builder(image_mock, mock_stream) as builder:
            builder.build()
            builder.save()

        posts = {post.remote_identifier: post for post in Post.objects.all()}

        self.assertCountEqual(("hr64xh", "hr4bxo", "hr14y5", "hr2fv0"), posts.keys())

        post = posts["hr64xh"]

        title = (
            "Ya’ll, I just can’t... this is my "
            "son, Judah. My wife and I have no "
            "idea how we created such a "
            "beautiful child."
        )
        url = "https://i.redd.it/cm2qybia1va51.jpg"

        self.assertEquals(
            "https://www.reddit.com/r/aww/comments/hr64xh/yall_i_just_cant_this_is_my_son_judah_my_wife_and/",
            post.url,
        )
        self.assertEquals(
            f"<div><img alt='{title}' src='{url}' loading='lazy' /></div>", post.body
        )

    def test_external_image_post(self):
        builder = RedditBuilder

        subreddit = SubredditFactory()
        mock_stream = Mock(rule=subreddit)

        with builder(external_image_mock, mock_stream) as builder:
            builder.build()
            builder.save()

        posts = {post.remote_identifier: post for post in Post.objects.all()}

        self.assertCountEqual(("hr41am", "huoldn"), posts.keys())

        post = posts["hr41am"]

        url = "http://gfycat.com/thatalivedogwoodclubgall"
        title = "Excited cows have a new brush!"

        self.assertEquals(
            f"<div><a target='_blank' rel='noopener noreferrer' alt='{title}' href='{url}' class='link'>Direct url</a></div>",
            post.body,
        )
        self.assertEquals(
            "https://www.reddit.com/r/aww/comments/hr41am/excited_cows_have_a_new_brush/",
            post.url,
        )

        post = posts["huoldn"]

        url = "https://i.imgur.com/usfMVUJ.jpg"
        title = "Novosibirsk Zoo welcomes 16 cobalt-eyed Pallas’s cat kittens"

        self.assertEquals(
            f"<div><img alt='{title}' src='{url}' loading='lazy' /></div>", post.body
        )
        self.assertEquals(
            "https://www.reddit.com/r/aww/comments/huoldn/novosibirsk_zoo_welcomes_16_cobalteyed_pallass/",
            post.url,
        )

    def test_video_post(self):
        builder = RedditBuilder

        subreddit = SubredditFactory()
        mock_stream = Mock(rule=subreddit)

        with builder(video_mock, mock_stream) as builder:
            builder.build()
            builder.save()

        posts = {post.remote_identifier: post for post in Post.objects.all()}

        self.assertCountEqual(("hr32jf", "hr1r00", "hqy0ny", "hr0uzh"), posts.keys())

        post = posts["hr1r00"]

        url = "https://v.redd.it/eyvbxaeqtta51/DASH_480.mp4?source=fallback"

        self.assertEquals(
            post.url,
            "https://www.reddit.com/r/aww/comments/hr1r00/cool_catt_and_his_clingy_girlfriend/",
        )
        self.assertEquals(
            f"<div><video controls muted><source src='{url}' type='video/mp4' /></video></div>",
            post.body,
        )

    def test_external_video_post(self):
        builder = RedditBuilder

        subreddit = SubredditFactory()
        mock_stream = Mock(rule=subreddit)

        with builder(external_video_mock, mock_stream) as builder:
            builder.build()
            builder.save()

        post = Post.objects.get()

        self.assertEquals(post.remote_identifier, "hulh8k")

        self.assertEquals(
            post.url,
            "https://www.reddit.com/r/aww/comments/hulh8k/dog_splashing_in_water/",
        )

        title = "Dog splashing in water"
        url = "https://gfycat.com/excellentinfantileamericanwigeon"

        self.assertEquals(
            f"<div><a target='_blank' rel='noopener noreferrer' alt='{title}' href='{url}' class='link'>Direct url</a></div>",
            post.body,
        )

    def test_external_gifv_video_post(self):
        builder = RedditBuilder

        subreddit = SubredditFactory()
        mock_stream = Mock(rule=subreddit)

        with builder(external_gifv_mock, mock_stream) as builder:
            builder.build()
            builder.save()

        post = Post.objects.get()

        self.assertEquals(post.remote_identifier, "humdlf")

        self.assertEquals(
            post.url, "https://www.reddit.com/r/aww/comments/humdlf/if_i_fits_i_sits/"
        )

        self.assertEquals(
            "<div><video controls muted><source src='https://i.imgur.com/grVh2AG.mp4' type='video/mp4' /></video></div>",
            post.body,
        )

    def test_link_only_post(self):
        builder = RedditBuilder

        subreddit = SubredditFactory()
        mock_stream = Mock(rule=subreddit)

        with builder(simple_mock, mock_stream) as builder:
            builder.build()
            builder.save()

        post = Post.objects.get(remote_identifier="hngsj8")

        title = "KeePassXC 2.6.0 released"
        url = "https://keepassxc.org/blog/2020-07-07-2.6.0-released/"

        self.assertIn(
            f"<div><a target='_blank' rel='noopener noreferrer' alt='{title}' href='{url}' class='link'>Direct url</a></div>",
            post.body,
        )

        self.assertEquals(
            post.url,
            "https://www.reddit.com/r/linux/comments/hngsj8/keepassxc_260_released/",
        )

    def test_skip_not_known_post_type(self):
        builder = RedditBuilder

        subreddit = SubredditFactory()
        mock_stream = Mock(rule=subreddit)

        with builder(unknown_mock, mock_stream) as builder:
            builder.build()
            builder.save()

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

    def test_nsfw_not_allowed(self):
        builder = RedditBuilder

        subreddit = SubredditFactory(reddit_allow_nfsw=False)
        mock_stream = Mock(rule=subreddit)

        with builder(nsfw_mock, mock_stream) as builder:
            builder.build()
            builder.save()

        posts = {post.remote_identifier: post for post in Post.objects.all()}

        self.assertEquals(Post.objects.count(), 1)
        self.assertCountEqual(("hna75r",), posts.keys())

    def test_spoiler_not_allowed(self):
        builder = RedditBuilder

        subreddit = SubredditFactory(reddit_allow_spoiler=False)
        mock_stream = Mock(rule=subreddit)

        with builder(spoiler_mock, mock_stream) as builder:
            builder.build()
            builder.save()

        posts = {post.remote_identifier: post for post in Post.objects.all()}

        self.assertEquals(Post.objects.count(), 1)
        self.assertCountEqual(("hm0qct",), posts.keys())

    def test_already_seen_not_allowed(self):
        builder = RedditBuilder

        subreddit = SubredditFactory(reddit_allow_viewed=False)
        mock_stream = Mock(rule=subreddit)

        with builder(seen_mock, mock_stream) as builder:
            builder.build()
            builder.save()

        posts = {post.remote_identifier: post for post in Post.objects.all()}

        self.assertEquals(Post.objects.count(), 1)
        self.assertCountEqual(("hna75r",), posts.keys())

    def test_upvote_minimum(self):
        builder = RedditBuilder

        subreddit = SubredditFactory(reddit_upvotes_min=100)
        mock_stream = Mock(rule=subreddit)

        with builder(upvote_mock, mock_stream) as builder:
            builder.build()
            builder.save()

        posts = {post.remote_identifier: post for post in Post.objects.all()}

        self.assertEquals(Post.objects.count(), 1)
        self.assertCountEqual(("hna75r",), posts.keys())

    def test_comments_minimum(self):
        builder = RedditBuilder

        subreddit = SubredditFactory(reddit_comments_min=100)
        mock_stream = Mock(rule=subreddit)

        with builder(comment_mock, mock_stream) as builder:
            builder.build()
            builder.save()

        posts = {post.remote_identifier: post for post in Post.objects.all()}

        self.assertEquals(Post.objects.count(), 1)
        self.assertCountEqual(("hm0qct",), posts.keys())

    def test_downvote_maximum(self):
        builder = RedditBuilder

        subreddit = SubredditFactory(reddit_downvotes_max=20)
        mock_stream = Mock(rule=subreddit)

        with builder(downvote_mock, mock_stream) as builder:
            builder.build()
            builder.save()

        posts = {post.remote_identifier: post for post in Post.objects.all()}

        self.assertEquals(Post.objects.count(), 1)
        self.assertCountEqual(("hm0qct",), posts.keys())
