from io import StringIO
import os

from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase
from django.test.client import RequestFactory
from rest_framework import status

from terra_settings.helpers import Choices, CustomCsvBuilder
from terra_settings.helpers.responses import get_media_response


class CSVTestCase(TestCase):
    def test_create_csv_1(self):
        data = {"test": "hello-world",
                "value": [
                    {"data": {
                        "test1": {
                            "first": {
                                "events": 1,
                                "participants": 1},
                            "second": {
                                "events": 2,
                                "participants": 2},
                            "third": {
                                "events": 1,
                                "participants": 101}
                        }
                    },
                        "name": "Name1"
                    },
                    {"data": {
                        "test0": {
                            "fourth": {
                                "events": 1,
                                "participants": 1
                            }
                        },
                        "test1": {
                            "first": {
                                "events": 1,
                                "participants": 1
                            },
                            "second": {
                                "events": 8,
                                "participants": 73
                            },
                            "third": {
                                "events": 1,
                                "participants": 101}
                        }
                    },
                        "name": "Name2"}
                ]
                }

        csv_builder = CustomCsvBuilder(data)

        output = StringIO()
        csv_builder.create_csv(output)
        list_lines = self.get_columns(output.read())
        self.assertCountEqual(
            list_lines,
            [['test', 'hello-world', ''],
             ['value', '', ''],
             ['value_data_test0_fourth_events', '', '1'],
             ['value_data_test0_fourth_participants', '', '1'],
             ['value_data_test1_first_events', '1', '1'],
             ['value_data_test1_first_participants', '1', '1'],
             ['value_data_test1_second_events', '2', '8'],
             ['value_data_test1_second_participants', '2', '73'],
             ['value_data_test1_third_events', '1', '1'],
             ['value_data_test1_third_participants', '101', '101'],
             ['value_name', 'Name1', 'Name2']])

    def test_create_csv_2(self):
        data = {"test": "hello-world", "test2": "HELLO-WORLD"}
        csv_builder = CustomCsvBuilder(data)
        output = StringIO()
        csv_builder.create_csv(output)
        list_lines = self.get_columns(output.read())
        self.assertCountEqual(
            list_lines,
            [['test', 'hello-world'], ['test2', 'HELLO-WORLD']])

    def get_columns(self, csv):
        list_csv = []
        tab = csv.split('\r\n')
        tab.pop(-1)
        for line in tab:
            values = line.split(',')
            list_csv.append(values)
        by_column = []
        for id in range(len(list_csv[0])):
            column = []
            for line in list_csv:
                column.append(line[id])
            by_column.append(column)
        return by_column


class ChoicesTests(TestCase):
    def setUp(self):
        self.MY_CHOICES = Choices(
            ('ONE', 1, 'One for the money'),
            ('TWO', 2, 'Two for the show'),
            ('THREE', 3, 'Three to get ready'),
        )
        self.MY_CHOICES.add_subset("ODD", ("ONE", "THREE"))

    """
    Testing the choices
    """

    def test_simple_choice(self):
        self.assertEqual(self.MY_CHOICES.CHOICES,
                         ((1, "One for the money"),
                          (2, "Two for the show"),
                          (3, "Three to get ready"),))
        self.assertEqual(self.MY_CHOICES.CHOICES_DICT,
                         {
                             1: 'One for the money',
                             2: 'Two for the show',
                             3: 'Three to get ready'
                         })
        self.assertEqual(self.MY_CHOICES.REVERTED_CHOICES_DICT,
                         {
                             'One for the money': 1,
                             'Three to get ready': 3,
                             'Two for the show': 2
                         })

    def test__contains__(self):
        self.failUnless(self.MY_CHOICES.ONE in self.MY_CHOICES)

    def test__iter__(self):
        self.assertEqual([k for k, v in self.MY_CHOICES], [1, 2, 3])

    def test_unique_values(self):
        self.assertRaises(ValueError, Choices,
                          ('TWO', 4, 'Deux'), ('FOUR', 4, 'Quatre'))

    def test_unique_constants(self):
        self.assertRaises(ValueError, Choices,
                          ('TWO', 2, 'Deux'), ('TWO', 4, 'Quatre'))

    def test_const_choice(self):
        self.assertEqual(self.MY_CHOICES.CONST_CHOICES,
                         (("ONE", "One for the money"),
                          ("TWO", "Two for the show"),
                          ("THREE", "Three to get ready"),))

    def test_value_to_const(self):
        self.assertEqual(self.MY_CHOICES.VALUE_TO_CONST,
                         {1: "ONE", 2: "TWO", 3: "THREE"})

    def test_add_should_add_in_correct_order(self):
        SOME_CHOICES = Choices(
            ('ONE', 1, 'One'),
            ('TWO', 2, 'Two'),
        )
        OTHER_CHOICES = Choices(
            ('THREE', 3, 'Three'),
            ('FOUR', 4, 'Four'),
        )
        # Adding a choices to choices
        tup = SOME_CHOICES + OTHER_CHOICES
        self.assertEqual(tup, ((1, 'One'), (2, 'Two'),
                               (3, 'Three'), (4, 'Four')))

        # Adding a tuple to choices
        tup = SOME_CHOICES + ((3, 'Three'), (4, 'Four'))
        self.assertEqual(tup, ((1, 'One'), (2, 'Two'),
                               (3, 'Three'), (4, 'Four')))

        """Adding a choices to tuple => do not work; is it possible to
           emulate it?
            tup = ((1, 'One'), (2, 'Two')) + OTHER_CHOICES
            self.assertEqual(tup, ((1, 'One'), (2, 'Two'),
                                   (3, 'Three'), (4, 'Four')))
        """

    def test_retrocompatibility(self):
        MY_CHOICES = Choices(
            ('TWO', 2, 'Deux'),
            ('FOUR', 4, 'Quatre'),
            name="EVEN"
        )
        MY_CHOICES.add_choices("ODD",
                               ('ONE', 1, 'Un'),
                               ('THREE', 3, 'Trois'), )
        self.assertEqual(MY_CHOICES.CHOICES, ((2, 'Deux'), (4, 'Quatre'),
                                              (1, 'Un'), (3, 'Trois')))
        self.assertEqual(MY_CHOICES.ODD, ((1, 'Un'), (3, 'Trois')))
        self.assertEqual(MY_CHOICES.EVEN, ((2, 'Deux'), (4, 'Quatre')))


class SubsetTests(TestCase):
    def setUp(self):
        self.MY_CHOICES = Choices(
            ('ONE', 1, 'One for the money'),
            ('TWO', 2, 'Two for the show'),
            ('THREE', 3, 'Three to get ready'),
        )
        self.MY_CHOICES.add_subset("ODD", ("ONE", "THREE"))
        self.MY_CHOICES.add_subset("ODD_BIS", ("ONE", "THREE"))

    def test_basic(self):
        self.assertEqual(self.MY_CHOICES.ODD, ((1, 'One for the money'),
                                               (3, 'Three to get ready')))

    def test__contains__(self):
        self.failUnless(self.MY_CHOICES.ONE in self.MY_CHOICES.ODD)

    def test__eq__(self):
        self.assertEqual(self.MY_CHOICES.ODD, ((1, 'One for the money'),
                                               (3, 'Three to get ready')))
        self.assertEqual(self.MY_CHOICES.ODD, self.MY_CHOICES.ODD_BIS)


class MediaResponseTest(TestCase):
    def setUp(self):
        self.factory = RequestFactory()

    def test_get_media_response_with_path(self):
        request = self.factory.get('fake/path')

        # Creating a real file
        # In this case, we test if get_media_response return content
        # from a file and so will use open()
        tmp_name = '/tmp/test.txt'
        with open(tmp_name, 'wb') as tmp_file:
            tmp_file.write(b"ceci n'est pas une pipe")

        response = get_media_response(
            request,
            {'path': tmp_name, 'url': None}  # url none, no accel-redirect
        )

        # deleting the file since we don't need it anymore
        os.remove(tmp_name)

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIsInstance(response.content, bytes)
        self.assertEqual(response.content, b"ceci n'est pas une pipe")

    def test_get_media_response_with_file_object(self):
        request = self.factory.get('fake/path')

        tmp_file = SimpleUploadedFile(name='/tmp/file.txt',
                                      content=b'creativity takes courage')

        # Adding fake url, we don't care, we are not testing accel-redirect
        tmp_file.url = None

        response = get_media_response(request, tmp_file)

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIsInstance(response.content, bytes)
        self.assertEqual(response.content, b'creativity takes courage')
