from datetime import datetime
from unittest.mock import patch
from uuid import uuid4

from django.test import TestCase
from django.utils import timezone

import pytz

from freezegun import freeze_time
from ftfy import fix_text

from newsreader.news.collection.choices import RuleTypeChoices
from newsreader.news.collection.exceptions import (
    StreamDeniedException,
    StreamForbiddenException,
    StreamNotFoundException,
    StreamTimeOutException,
)
from newsreader.news.collection.tests.factories import TwitterTimelineFactory
from newsreader.news.collection.tests.twitter.collector.mocks import (
    empty_mock,
    simple_mock,
)
from newsreader.news.collection.twitter import TWITTER_URL, TwitterCollector
from newsreader.news.collection.utils import truncate_text
from newsreader.news.core.models import Post


@freeze_time("2020-09-26 14:40:00")
class TwitterCollectorTestCase(TestCase):
    def setUp(self):
        patched_get = patch("newsreader.news.collection.twitter.fetch")
        self.mocked_fetch = patched_get.start()

        patched_parse = patch("newsreader.news.collection.twitter.TwitterStream.parse")
        self.mocked_parse = patched_parse.start()

    def tearDown(self):
        patch.stopall()

    def test_simple_batch(self):
        self.mocked_parse.return_value = simple_mock

        timeline = TwitterTimelineFactory(
            user__twitter_oauth_token=str(uuid4()),
            user__twitter_oauth_token_secret=str(uuid4()),
            screen_name="RobertsSpaceInd",
            enabled=True,
        )

        collector = TwitterCollector()
        collector.collect(rules=[timeline])

        self.assertCountEqual(
            Post.objects.values_list("remote_identifier", flat=True),
            ("1307054882210435074", "1307029168941461504"),
        )

        self.assertEquals(timeline.succeeded, True)
        self.assertEquals(timeline.last_run, timezone.now())
        self.assertIsNone(timeline.error)

        post = Post.objects.get(
            remote_identifier="1307054882210435074",
            rule__type=RuleTypeChoices.twitter_timeline,
        )

        self.assertEquals(
            post.publication_date, pytz.utc.localize(datetime(2020, 9, 18, 20, 32, 22))
        )

        title = truncate_text(
            Post,
            "title",
            "It's a close match-up for #SCShipShowdown today! Which Aegis ship"
            " do you think will make it to the Semi-Finals?",
        )

        self.assertEquals(post.author, "RobertsSpaceInd")
        self.assertEquals(post.title, title)
        self.assertEquals(
            post.url, f"{TWITTER_URL}/RobertsSpaceInd/status/1307054882210435074"
        )

        post = Post.objects.get(
            remote_identifier="1307029168941461504",
            rule__type=RuleTypeChoices.twitter_timeline,
        )

        self.assertEquals(
            post.publication_date, pytz.utc.localize(datetime(2020, 9, 18, 18, 50, 11))
        )

        body = fix_text(
            "We\u2019re welcoming members of our Builds, Publishes and Platform"
            " teams on Star Citizen Live to talk about the process involved in"
            " bringing everyone\u2019s work together and getting it out into your"
            " hands. Going live on #Twitch in 10 minutes."
            " \ud83c\udfa5\ud83d\udd34 \n\nTune in:"
            " https://t.co/2AdNovhpFW https://t.co/Cey5JpR1i9"
        )

        title = truncate_text(Post, "title", body)

        self.assertEquals(post.author, "RobertsSpaceInd")
        self.assertEquals(post.title, title)
        self.assertEquals(
            post.url, f"{TWITTER_URL}/RobertsSpaceInd/status/1307029168941461504"
        )

    def test_empty_batch(self):
        self.mocked_parse.return_value = empty_mock

        timeline = TwitterTimelineFactory()

        collector = TwitterCollector()
        collector.collect(rules=[timeline])

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

        self.assertEquals(timeline.succeeded, True)
        self.assertEquals(timeline.last_run, timezone.now())
        self.assertIsNone(timeline.error)

    def test_not_found(self):
        self.mocked_fetch.side_effect = StreamNotFoundException

        timeline = TwitterTimelineFactory()

        collector = TwitterCollector()
        collector.collect(rules=[timeline])

        self.assertEquals(Post.objects.count(), 0)
        self.assertEquals(timeline.succeeded, False)
        self.assertEquals(timeline.error, "Stream not found")

    def test_denied(self):
        self.mocked_fetch.side_effect = StreamDeniedException

        timeline = TwitterTimelineFactory(
            user__twitter_oauth_token=str(uuid4()),
            user__twitter_oauth_token_secret=str(uuid4()),
        )

        collector = TwitterCollector()
        collector.collect(rules=[timeline])

        self.assertEquals(Post.objects.count(), 0)
        self.assertEquals(timeline.succeeded, False)
        self.assertEquals(timeline.error, "Stream does not have sufficient permissions")

        user = timeline.user

        self.assertIsNone(user.twitter_oauth_token)
        self.assertIsNone(user.twitter_oauth_token_secret)

    def test_forbidden(self):
        self.mocked_fetch.side_effect = StreamForbiddenException

        timeline = TwitterTimelineFactory()

        collector = TwitterCollector()
        collector.collect(rules=[timeline])

        self.assertEquals(Post.objects.count(), 0)
        self.assertEquals(timeline.succeeded, False)
        self.assertEquals(timeline.error, "Stream forbidden")

    def test_timed_out(self):
        self.mocked_fetch.side_effect = StreamTimeOutException

        timeline = TwitterTimelineFactory()

        collector = TwitterCollector()
        collector.collect(rules=[timeline])

        self.assertEquals(Post.objects.count(), 0)
        self.assertEquals(timeline.succeeded, False)
        self.assertEquals(timeline.error, "Stream timed out")
