# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from copy import copy

from django.contrib.auth.models import Permission
from django.test import TestCase, Client
from django.core.paginator import EmptyPage
from bs4 import BeautifulSoup

from utils import create_tests_data, delete_tests_data
from newsbox.urls import get_urls
from .models import News, NewsSEO, NewsExpired, NewsComplete, NewsExtended


class NewsboxAutoURLsBaseTests(object):

    def setUp(self):
        self.current_app_name = self.__module__.split('.')[0]
        super(NewsboxAutoURLsBaseTests, self).setUp()

    def test_01_auto_urls(self):
        expected_model_names = copy(self.expected_model_names)
        urls = get_urls()

        self.assertEqual(len(urls), len(expected_model_names))
        for master_url in urls:
            self.assertTrue(hasattr(master_url, 'url_patterns'))
            self.assertEqual(master_url.namespace, self.current_app_name)
            # 5 urls : archive, year, month, day, detail
            self.assertEqual(len(master_url.url_patterns), 5)
            current_model_name = None
            nb_list = nb_detail = 0
            for url in master_url.url_patterns:
                model_name, action_name = url.name.split('_', )
                if not current_model_name:
                    current_model_name = model_name
                    self.assertTrue(current_model_name in expected_model_names)
                    expected_model_names.remove(current_model_name)
                else:
                    # all suburls must be for the same model
                    self.assertEqual(current_model_name, model_name)
                self.assertTrue(action_name in ('list', 'detail'))
                if action_name == 'list':
                    nb_list += 1
                else:
                    nb_detail += 1
            self.assertEqual(nb_detail, 1)  # only one detail url
            self.assertEqual(nb_list, 4)  # 4 list urls : archive, year, month, day


class NewsboxAutoURLsTests(NewsboxAutoURLsBaseTests, TestCase):

    def setUp(self):
        self.expected_model_names = [News._meta.model_name, NewsSEO._meta.model_name,
                                     NewsExpired._meta.model_name, NewsComplete._meta.model_name,
                                     NewsExtended._meta.model_name]
        super(NewsboxAutoURLsTests, self).setUp()


class NewsboxModelsTests(TestCase):

    def setUp(self):
        create_tests_data('myapp')

    def tearDown(self):
        delete_tests_data('myapp')

    # fixtures = ['tests_data.json', ]

    def has_readmore_link(self, news):
        return bool(news.find('a', class_='readmore'))

    def get_news_in_list_by_summary(self, response, summary):
        soup = BeautifulSoup(response.content, "html.parser")
        for news in soup.find_all('article'):
            readmore_summary = news.find('p', text=summary)
            if readmore_summary:
                return news
        return None

    def test_01_manager_published(self):
        """
        Test if we only get really published news via the manager
        """
        newsClasses = [News, NewsSEO, NewsExpired, NewsComplete, NewsExtended, ]
        for cls in newsClasses:
            returned_ids = list(cls.objects.published().values_list(
                'id', flat=True).order_by('id'))
            self.assertEqual((cls.__name__, returned_ids),
                             (cls.__name__, [1, 4, 6, 7, ]))

    def test_02_view_archive_200(self):
        """
        Test if generic archive view return right informations.
        """
        c = Client()
        r = c.get('/myapp/news/')
        self.assertEqual(r.status_code, 200)
        self.assertIn('object_list', r.context)
        self.assertEqual(len(r.context['object_list']), 1)
        self.assertEqual(r.context['page_obj'].number, 1)
        self.assertEqual(r.context['paginator'].num_pages, 4)
        self.assertFalse(r.context['page_obj'].has_previous())
        self.assertTrue(r.context['page_obj'].has_next())
        with self.assertRaises(EmptyPage):
            r.context['page_obj'].previous_page_number()
        self.assertEqual(r.context['page_obj'].next_page_number(), 2)

    def test_03_view_year_archive_200(self):
        """
        Test if generic year views return right informations.
        """
        c = Client()
        r = c.get('/myapp/news/2005/')
        self.assertEqual(r.status_code, 200)
        self.assertEqual(r.context['object_list'][0].pk, 6)
        self.assertFalse(r.context['is_paginated'])
        r = c.get('/myapp/news/2014/')
        self.assertEqual(r.status_code, 200)
        self.assertEqual(r.context['paginator'].num_pages, 3)
        r = c.get('/myapp/news/2020/')
        self.assertEqual(r.status_code, 404)

    def test_04_view_year_archive_404(self):
        """
        Test if generic year views return a 404 with an URL with a year without
        any news.
        """
        c = Client()
        r = c.get('/myapp/news/1984/')
        self.assertEqual(r.status_code, 404)

    def test_05_view_month_archive_200(self):
        """
        Test if generic month views return right informations.
        """
        c = Client()
        r = c.get('/myapp/news/2005/07/')
        self.assertEqual(r.status_code, 200)
        self.assertEqual(r.context['object_list'].count(), 1)
        self.assertEqual(len(r.context['date_list']), 1)
        self.assertEqual(r.context['date_list'][0].year, 2005)
        self.assertEqual(r.context['date_list'][0].month, 7)
        self.assertEqual(r.context['current_date'],
                         r.context['object_list'][0].newsbox_publication_start_date)

    def test_06_view_month_archive_404(self):
        """
        Test if generic month views return a 404 with an URL with a year + month
        without any news.
        """
        c = Client()
        r = c.get('/myapp/news/2005/08/')
        self.assertEqual(r.status_code, 404)

    def test_07_view_day_archive_200(self):
        """
        Test if generic day views return right informations.
        """
        c = Client()
        r = c.get('/myapp/news/2005/07/15/')
        self.assertEqual(r.status_code, 200)
        self.assertEqual(r.context['object_list'].count(), 1)
        self.assertEqual(r.context['day'].year, 2005)
        self.assertEqual(r.context['day'].month, 7)
        self.assertEqual(r.context['day'].day, 15)
        self.assertEqual(r.context['current_date'],
                         r.context['object_list'][0].newsbox_publication_start_date)

    def test_08_view_day_archive_404(self):
        """
        Test if generic day views return a 404 with an URL with a date without
        any news.
        """
        c = Client()
        r = c.get('/myapp/news/2005/07/14/')
        self.assertEqual(r.status_code, 404)

    def test_09_view_detail_200(self):
        """
        Test if generic day views return right informations.
        """
        c = Client()
        r = c.get('/myapp/news/2005/07/15/news-6/')
        self.assertEqual(r.status_code, 200)
        self.assertEqual(r.context['object'].pk, 6)

    def test_10_view_detail_404(self):
        """
        Test if generic day views return a 404 with an URL with an invalid slug
        """
        c = Client()
        r = c.get('/myapp/news/2005/07/15/my-birthday/')
        self.assertEqual(r.status_code, 404)

    def test_11_view_detail_404_not_published(self):
        """
        Test if generic day views return a 404 with an URL with a not published
        news
        """
        c = Client()
        r = c.get('/myapp/news/2005/07/15/news-2/')
        self.assertEqual(r.status_code, 404)

    def test_12_view_detail_404_wrong_date(self):
        """
        Test if generic day views return a 404 with an URL with an invalide date
        """
        c = Client()
        r = c.get('/myapp/news/1984/04/01/news-6/')
        self.assertEqual(r.status_code, 404)

    def test_13_news_are_ordered(self):
        """
        Test if news are correctly ordered
        """
        c = Client()
        r = c.get('/myapp/news/?page=1')
        news = self.get_news_in_list_by_summary(r, 'Summary of the news 7')
        self.assertIsNotNone(news)
        r = c.get('/myapp/news/?page=2')
        news = self.get_news_in_list_by_summary(r, 'Summary of the news 4')
        self.assertIsNotNone(news)
        r = c.get('/myapp/news/?page=3')
        news = self.get_news_in_list_by_summary(r, 'Summary of the news 1')
        self.assertIsNotNone(news)
        r = c.get('/myapp/news/?page=4')
        news = self.get_news_in_list_by_summary(r, 'Summary of the news 6')
        self.assertIsNotNone(news)

    def test_14_news_without_no_readmore_link(self):
        """
        Test if readmore link is not present in news 4
        """
        c = Client()
        r = c.get('/myapp/news/?page=2')
        news = self.get_news_in_list_by_summary(r, 'Summary of the news 4')
        self.assertIsNotNone(news)

        found = self.has_readmore_link(news)
        self.assertFalse(found)

    def test_15_news_with_readmore_link(self):
        """
        Test if readmore link is present in news 6
        """
        c = Client()
        r = c.get('/myapp/news/2005/')
        news = self.get_news_in_list_by_summary(r, 'Summary of the news 6')
        self.assertIsNotNone(news)
        found = self.has_readmore_link(news)
        self.assertTrue(found)

    def test_16_is_published_property(self):
        """
        Test if is_published property is similar to the q_published method
        """
        for model in [News, NewsSEO, NewsExpired, NewsComplete, NewsExtended]:
            for news in model.objects.filter(model.q_published()):
                self.assertTrue(news.is_published)
            for news in model.objects.exclude(model.q_published()):
                self.assertFalse(news.is_published)

    # def test_17_permissions(self):
        # """
        # Test if permissions are correctly created for all models
        # """
        # for model in [News, NewsSEO, NewsExpired, NewsComplete, NewsExtended]:
            # Permission.objects.filter(content_type__app_label='myapp',
                                      # content_type__model=model._meta.model_name)