"""This module contains the tests for the views of the ``brainstormers`` app"""
import csv
from io import StringIO, BytesIO

from django.test import override_settings

from mtp.apps.common.utils import EXCEL_BOM
from mtp.apps.import_export.xls import XLSReader

from ..constants import BRAINSTORMER_TYPE_CHOICES
from ..factories import (BrainstormerFactory,
                         BrainstormerPropositionGroupFactory,
                         BrainstormerPropositionFactory)
from ..models import BrainstormerTemplate

from .test_cases import BrainstormerTestCase


class BrainstormerViewTestCase(BrainstormerTestCase):
    """Test the views related to the ``Brainstormer`` model"""

    @classmethod
    def setUpTestData(cls):
        """Set up the test data"""
        super().setUpTestData()

        # We will need a brainstormer for the session
        cls.brainstormer = BrainstormerFactory()
        cls.group = BrainstormerPropositionGroupFactory(brainstormer=cls.brainstormer)

    def test_all_routes_should_work(self):
        """Test the response_code of all the brainstormer related routes"""

        self.login_as_trainer()

        urls_to_test = [
            self.training_session.get_brainstormer_import_url(),
            self.brainstormer.get_detail_url(),
            self.brainstormer.get_update_url(),
            self.brainstormer.get_delete_url(),
            self.brainstormer.get_create_proposition_url(),
        ]

        for brainstormer_type in BRAINSTORMER_TYPE_CHOICES:
            urls_to_test.append(
                self.training_session.get_brainstormer_create_url(
                    brainstormer_type[0]
                )
            )

        for url in self.training_session.get_brainstormer_type_list_urls():
            urls_to_test.append(url['url'])

        for url in [self.training_session.get_brainstorming_create_url(),
                    self.training_session.get_questionwall_create_url(),
                    self.training_session.get_wordcloud_create_url()]:

            self.assertIn(url, urls_to_test)

        for url in urls_to_test:
            response = self.client.get(url)
            self.assertEqual(response.status_code, 200)

    def test_brainstormer_export(self):
        """Test the response_code of all the export related routes."""
        self.login_as_trainer()
        for brainstormer_type in [BRAINSTORMER_TYPE_CHOICES.PROPOSITIONWALL,
                                  BRAINSTORMER_TYPE_CHOICES.QUESTIONWALL]:
            with self.subTest(brainstormer_type=brainstormer_type):
                self.brainstormer.brainstormer_type = brainstormer_type
                self.brainstormer.save()

                self.brainstormer = self.brainstormer.proxified()

                urls_to_test = [
                    self.brainstormer.get_export_xls_url(),
                    self.brainstormer.get_export_csv_url(),
                ]

                for url in urls_to_test:
                    response = self.client.get(url)
                    self.assertEqual(response.status_code, 200)

    def test_brainstormer_types_details(self):
        """Test the response code of each `Brainstormer.brainstormer_type`."""
        self.login_as_trainer()

        for choice in BRAINSTORMER_TYPE_CHOICES.choices:

            self.brainstormer.brainstormer_type = choice[0]
            # we need to save the object in order to make `get_object` work as expected
            self.brainstormer.save()
            url = self.brainstormer.get_detail_url()
            response = self.client.get(url)
            self.assertEqual(response.status_code, 200)

        # PROPOSITIONWALL and QUESTIONWALL can create propositions on the csm.
        # Test the QUESTIONWALL get_create_proposition_url
        self.brainstormer.brainstormer_type = BRAINSTORMER_TYPE_CHOICES.QUESTIONWALL
        url = self.brainstormer.proxified().get_create_proposition_url()
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)

    def test_create_brainstormer_not_allowed_on_closed_session(self):
        """A brainstormer can not be created on a closed session"""

        with override_settings(INSTANCE_IS_MASTER=True):
            self.training_session.close()

        create_url = self.training_session.get_brainstormer_create_url(
            BRAINSTORMER_TYPE_CHOICES.PROPOSITIONWALL
        )

        # The GET method for creation should raise 403 on closed session
        response = self.client.get(create_url)
        self.assertEqual(response.status_code, 403)

        # The POST method for creation should raise 403 on closed session
        response = self.client.post(create_url, {})
        self.assertEqual(response.status_code, 403)

    def test_import_brainstormer_not_allowed_on_closed_session(self):
        """A metaplan can not be created on a closed session"""

        with override_settings(INSTANCE_IS_MASTER=True):
            self.training_session.close()

        import_url = self.training_session.get_brainstormer_import_url()

        # The GET method for import should raise 403 on closed session
        response = self.client.get(import_url)
        self.assertEqual(response.status_code, 403)

        # The POST method for import should raise 403 on closed session
        response = self.client.post(import_url, {})
        self.assertEqual(response.status_code, 403)


class BrainstormerExportTestCase(BrainstormerTestCase):
    """Test the ``Brainstormer`` exporters. Both xls and csv."""

    @classmethod
    def setUpTestData(cls):
        """Set up the test data"""
        super().setUpTestData()

        # We will need a brainstormer for the session
        brainstormer = BrainstormerFactory()
        cls.proposition_wall = brainstormer.proxified()
        cls.group = BrainstormerPropositionGroupFactory(brainstormer=cls.proposition_wall)
        cls.propositions = BrainstormerPropositionFactory.create_batch(
            2,
            brainstormer=cls.proposition_wall)

        cls.propositions[0].groups.add(cls.group)

        question_wall = BrainstormerFactory(
            brainstormer_type=BRAINSTORMER_TYPE_CHOICES.QUESTIONWALL
        )
        cls.question_wall = question_wall.proxified()
        BrainstormerPropositionFactory.create_batch(
            2,
            brainstormer=cls.question_wall,
        )

    def test_brainstormer_export_csv(self):
        """Test the csv exporter of a `Brainstormer`."""
        self.login_as_trainer()
        # Only propositionwalls can be exported to csv with groups
        url = self.proposition_wall.get_export_csv_url()
        response = self.client.get(url)
        file_descriptor = StringIO(response.content.decode('utf-8'))
        reader = csv.reader(file_descriptor, delimiter=';')
        rows = [row for row in reader]

        # Do not forget the EXCEL_BOM
        self.assertEqual(rows[0][0], EXCEL_BOM)
        # A title

        self.assertEqual(
            rows[1],
            [
                'Export %(brainstormer_type)s %(name)s %(start_date)s' % {
                    'brainstormer_type': BRAINSTORMER_TYPE_CHOICES.for_value(
                        self.proposition_wall.brainstormer_type
                    ).display,
                    'name': self.proposition_wall.session.training.name,
                    'start_date': self.proposition_wall.session.start_date
                },
                self.proposition_wall.name
            ]
        )
        # A blank row
        self.assertEqual(rows[2], [])
        # Name of the group and number of propositions in it.
        propositions_count = self.group.propositions.all().count()
        self.assertEqual(
            rows[3],
            ['Group', self.group.name, '{} propositions'.format(propositions_count), 'like(s)']
        )
        self.assertEqual(
            rows[4], [
                '1',
                self.propositions[0].text,
                self.propositions[0].user.first_name,
                self.propositions[0].user.last_name,
                str(self.propositions[0].likes.count())
            ]
        )
        # Ungrouped propositions header
        self.assertEqual(rows[5], ['Group', 'ungrouped', '1 propositions', 'like(s)'])
        self.assertEqual(
            rows[6],
            [
                '1',
                self.propositions[1].text,
                self.propositions[1].user.first_name,
                self.propositions[1].user.last_name,
                str(self.propositions[1].likes.count())
            ]
        )

        self.login_as_trainer()
        # Questionwalls can be exported to csv with propositions
        url = self.question_wall.get_export_csv_url()
        response = self.client.get(url)
        file_descriptor = StringIO(response.content.decode('utf-8'))
        reader = csv.reader(file_descriptor, delimiter=';')
        rows = [row for row in reader]

        # Do not forget the EXCEL_BOM
        self.assertEqual(rows[0][0], EXCEL_BOM)
        # A title
        self.assertEqual(
            rows[1],
            [
                'Export %(brainstormer_type)s %(name)s %(start_date)s' % {
                    'brainstormer_type': BRAINSTORMER_TYPE_CHOICES.for_value(
                        self.question_wall.brainstormer_type
                    ).display,
                    'name': self.question_wall.session.training.name,
                    'start_date': self.question_wall.session.start_date
                },
                self.question_wall.name
            ]
        )

        # A blank row
        self.assertEqual(rows[2], [])
        # propositions
        self.assertEqual(
            rows[3],
            [
                '1',
                self.question_wall.get_propositions()[0].text,
                self.question_wall.get_propositions()[0].user.first_name,
                self.question_wall.get_propositions()[0].user.last_name,
                str(self.question_wall.get_propositions()[0].likes.count()),
            ]
        )
        self.assertEqual(
            rows[4],
            [
                '2',
                self.question_wall.get_propositions()[1].text,
                self.question_wall.get_propositions()[1].user.first_name,
                self.question_wall.get_propositions()[1].user.last_name,
                str(self.question_wall.get_propositions()[1].likes.count()),
            ]
        )

    def test_questionwall_export_xls(self):
        """Test the xls exporter of a ``PropositionWall``."""
        self.login_as_trainer()

        url = self.question_wall.get_export_xls_url()
        response = self.client.get(url)
        file_descriptor = BytesIO(response.content)
        reader = XLSReader(file_descriptor)
        reader.initialize()
        sheet_names = reader.workbook.get_sheet_names()

        self.assertEqual(len(sheet_names), 1)

        sheet = sheet_names[0]

        self.assertEqual(reader.read_row(sheet), {'B': 'QUESTIONWALL TITLE'})
        self.assertEqual(reader.read_row(sheet), {'B': self.question_wall.name})
        self.assertEqual(reader.read_row(sheet), {})
        # group header
        self.assertEqual(
            reader.read_row(sheet),
            {
                'D': 'AUTHOR FIRSTNAME',
                'C': 'LIKES',
                'B': 'PROPOSITION',
                'E': 'LASTNAME'
            }
        )
        self.assertEqual(
            reader.read_row(sheet),
            {
                'B': self.question_wall.get_propositions()[0].text,
                'C': str(self.question_wall.get_propositions()[0].likes.count()),
                'D': self.question_wall.get_propositions()[0].user.first_name,
                'E': self.question_wall.get_propositions()[0].user.last_name

            }
        )

        self.assertEqual(
            reader.read_row(sheet),
            {
                'B': self.question_wall.get_propositions()[1].text,
                'C': str(self.question_wall.get_propositions()[1].likes.count()),
                'D': self.question_wall.get_propositions()[1].user.first_name,
                'E': self.question_wall.get_propositions()[1].user.last_name
            }
        )
        self.assertEqual(reader.read_row(sheet), {})


    def test_propositionwall_export_xls(self):
        """Test the xls exporter of a ``PropositionWall``."""
        self.login_as_trainer()

        url = self.proposition_wall.get_export_xls_url()
        response = self.client.get(url)
        file_descriptor = BytesIO(response.content)
        reader = XLSReader(file_descriptor)
        reader.initialize()
        sheet_names = reader.workbook.get_sheet_names()

        self.assertEqual(len(sheet_names), 1)

        sheet = sheet_names[0]
        # iterate over the rows and check content
        self.assertEqual(reader.read_row(sheet), {'B': 'PROPOSITIONWALL TITLE'})
        self.assertEqual(reader.read_row(sheet), {'B': self.proposition_wall.name})
        self.assertEqual(reader.read_row(sheet), {})

        self.assertEqual(
            reader.read_row(sheet),
            {
                'D': 'LIKES',
                'B': 'GROUP NAME',
                'F': 'LASTNAME',
                'E': 'AUTHOR FIRSTNAME',
                'C': 'PROPOSITION'
            }
        )
        self.assertEqual(reader.read_row(sheet), {'B': self.proposition_wall.groups.first().name})
        self.assertEqual(
            reader.read_row(sheet),
            {
                'C': self.propositions[0].text,
                'D': str(self.propositions[0].likes.count()),
                'E': self.propositions[0].user.first_name,
                'F': self.propositions[0].user.last_name,
            }
        )

        # group header
        self.assertEqual(reader.read_row(sheet), {'B': 'No Group'})

        self.assertEqual(
            reader.read_row(sheet),
            {
                'C': self.propositions[1].text,
                'D': str(self.propositions[1].likes.count()),
                'E': self.propositions[1].user.first_name,
                'F': self.propositions[1].user.last_name,
            }
        )


class BrainstormerTemplateViewTestCase(BrainstormerTestCase):
    """Test the views related to the ``BrainstormerTemplate`` model."""

    def test_create_brainstormer_template(self):
        """Test creation of BrainstormerTemplates of divers brainstormer_type."""
        self.login_as_trainer()

        for choice in BRAINSTORMER_TYPE_CHOICES:
            with self.subTest(choice=choice):
                url = BrainstormerTemplate.get_create_url(
                    self.training,
                    choice[0]
                )
                # Test GET
                response = self.client.get(url)
                self.assertEqual(response.status_code, 200)

                # Test POST
                self.client.post(url, {"name": "template_%s" % choice[0]})
                self.assertEqual(response.status_code, 200)
                self.assertTrue(
                    BrainstormerTemplate.objects.filter(
                        name="template_%s" % choice[0],
                        brainstormer_type=choice[0]
                    ).exists()
                )
        # test a wrong brainstormer_type
        url = "/cms/trainings/%s/brainstormer-templates/%s/" % (self.training.pk, "z")
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

        self.client.post(url, {"name": "template_that_does_not_exists"})
        self.assertEqual(response.status_code, 404)
