from unittest.mock import Mock, patch

from django.test import TestCase

from bs4 import BeautifulSoup

from newsreader.news.collection.exceptions import (
    StreamDeniedException,
    StreamException,
    StreamForbiddenException,
    StreamNotFoundException,
    StreamParseException,
    StreamTimeOutException,
)
from newsreader.news.collection.favicon import WebsiteStream, WebsiteURLBuilder
from newsreader.news.collection.tests.factories import CollectionRuleFactory

from .mocks import feed_mock_without_link, simple_feed_mock, simple_mock


class WebsiteStreamTestCase(TestCase):
    def setUp(self):
        self.patched_fetch = patch("newsreader.news.collection.favicon.fetch")
        self.mocked_fetch = self.patched_fetch.start()

    def tearDown(self):
        patch.stopall()

    def test_simple(self):
        self.mocked_fetch.return_value = Mock(content=simple_mock)

        rule = CollectionRuleFactory(website_url="https://www.bbc.co.uk/news/")
        stream = WebsiteStream(rule)
        return_value = stream.read()

        self.mocked_fetch.assert_called_once_with("https://www.bbc.co.uk/news/")
        self.assertEquals(
            return_value, (BeautifulSoup(simple_mock, features="lxml"), stream)
        )

    def test_raises_exception(self):
        self.mocked_fetch.side_effect = StreamException

        rule = CollectionRuleFactory(website_url="https://www.bbc.co.uk/news/")
        stream = WebsiteStream(rule)

        with self.assertRaises(StreamException):
            stream.read()

        self.mocked_fetch.assert_called_once_with("https://www.bbc.co.uk/news/")

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

        rule = CollectionRuleFactory(website_url="https://www.bbc.co.uk/news/")
        stream = WebsiteStream(rule)

        with self.assertRaises(StreamDeniedException):
            stream.read()

        self.mocked_fetch.assert_called_once_with("https://www.bbc.co.uk/news/")

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

        rule = CollectionRuleFactory(website_url="https://www.bbc.co.uk/news/")
        stream = WebsiteStream(rule)

        with self.assertRaises(StreamNotFoundException):
            stream.read()

        self.mocked_fetch.assert_called_once_with("https://www.bbc.co.uk/news/")

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

        rule = CollectionRuleFactory(website_url="https://www.bbc.co.uk/news/")
        stream = WebsiteStream(rule)

        with self.assertRaises(StreamTimeOutException):
            stream.read()

        self.mocked_fetch.assert_called_once_with("https://www.bbc.co.uk/news/")

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

        rule = CollectionRuleFactory(website_url="https://www.bbc.co.uk/news/")
        stream = WebsiteStream(rule)

        with self.assertRaises(StreamForbiddenException):
            stream.read()

        self.mocked_fetch.assert_called_once_with("https://www.bbc.co.uk/news/")

    @patch("newsreader.news.collection.favicon.WebsiteStream.parse")
    def test_stream_raises_parse_exception(self, mocked_parse):
        self.mocked_fetch.return_value = Mock()
        mocked_parse.side_effect = StreamParseException

        rule = CollectionRuleFactory(website_url="https://www.bbc.co.uk/news/")
        stream = WebsiteStream(rule)

        with self.assertRaises(StreamParseException):
            stream.read()

        self.mocked_fetch.assert_called_once_with("https://www.bbc.co.uk/news/")


class WebsiteURLBuilderTestCase(TestCase):
    def test_simple(self):
        initial_rule = CollectionRuleFactory()

        with WebsiteURLBuilder(simple_feed_mock, Mock(rule=initial_rule)) as builder:
            builder.build()
            builder.save()

        initial_rule.refresh_from_db()

        self.assertEquals(initial_rule.website_url, "https://www.bbc.co.uk/news/")

    def test_no_link(self):
        initial_rule = CollectionRuleFactory(website_url=None)

        with WebsiteURLBuilder(
            feed_mock_without_link, Mock(rule=initial_rule)
        ) as builder:
            builder.build()
            builder.save()

        initial_rule.refresh_from_db()

        self.assertEquals(initial_rule.website_url, None)

    def test_no_data(self):
        initial_rule = CollectionRuleFactory(website_url=None)

        with WebsiteURLBuilder(None, Mock(rule=initial_rule)) as builder:
            builder.build()
            builder.save()

        initial_rule.refresh_from_db()

        self.assertEquals(initial_rule.website_url, None)
