"""This module contains the test cases for the ``data_export`` app"""

import io
import os

from mock import patch
import pandas as pd

from django.conf import settings
from django.utils.text import Truncator

from mtp.apps.common.tests.test_cases import WithMediaOnDisk

from .constants import EXCEL_STYLE_CHOICES
from .exporters import ExcelDataExporter


class ExcelDataExporterTestCase(WithMediaOnDisk):
    """Test case for the ``ExcelDataExporter``"""

    @classmethod
    def setUpTestData(cls):
        """Store a simple ``DataFrame"""

        super().setUpTestData()

        cls.data_frame = pd.DataFrame({'test': ['test']}, index=['test'])

    def test_save_must_output_a_path_if_initialized_with_a_path(self):
        """Test that the ``save`` method must output a path if initialized with a path"""

        path = os.path.join(settings.MEDIA_ROOT, 'excel_file.xlsx')
        self.assertFalse(os.path.isfile(path))

        exporter = ExcelDataExporter(path)
        exporter.save()

        self.assertTrue(os.path.isfile(path))

    def test_save_must_output_data_if_initialized_without_a_path(self):
        """Test that the ``save`` method must output data if initialized without a path"""

        exporter = ExcelDataExporter()

        data = exporter.save()

        self.assertIsInstance(data, io.BytesIO)

    def test_add_sheet_must_raise_if_styles_length_dont_match_the_columns_length(self):
        """Test that the ``add_sheet`` must raise if the styles and columns lengths mismatch"""

        exporter = ExcelDataExporter()

        # We got one column, but two styles
        with self.assertRaises(AssertionError):
            exporter.add_sheet(
                'test',
                self.data_frame,
                [EXCEL_STYLE_CHOICES.DEFAULT, EXCEL_STYLE_CHOICES.DEFAULT]
            )

        # This must work
        exporter.add_sheet(
            'test',
            self.data_frame,
            [EXCEL_STYLE_CHOICES.DEFAULT]
        )

    def test_add_sheet_must_add_a_sheet_to_the_workbook(self):
        """Test that the ``add_sheet`` method must add a sheet to the workbook"""

        exporter = ExcelDataExporter()

        self.assertEqual(len(exporter.excel_writer.sheets), 0)

        sheet_name = 'test_sheet'
        exporter.add_sheet(
            sheet_name,
            self.data_frame,
            [EXCEL_STYLE_CHOICES.DEFAULT]
        )

        self.assertEqual(len(exporter.excel_writer.sheets), 1)

    def test_add_sheet_with_illegal_characters_must_work(self):
        """Test that adding a sheet name with illegal characters is working"""

        exporter = ExcelDataExporter()

        self.assertEqual(len(exporter.excel_writer.sheets), 0)

        sheet_name = 'test_sheet_\/*[]:?'  # pylint: disable=anomalous-backslash-in-string
        exporter.add_sheet(
            sheet_name,
            self.data_frame,
            [EXCEL_STYLE_CHOICES.DEFAULT]
        )

        self.assertEqual(len(exporter.excel_writer.sheets), 1)

        # The sheet was created, but the illegal characters were removed
        self.assertIn('test_sheet_', exporter.excel_writer.sheets)

    def test_freeze_panes_option_must_work(self):
        """Test that using the freeze_panes should work"""

        exporter = ExcelDataExporter()

        with patch('xlsxwriter.worksheet.Worksheet.freeze_panes') as mocked:
            exporter.add_sheet(
                'sheet_name',
                self.data_frame,
                [EXCEL_STYLE_CHOICES.DEFAULT],
                freeze_panes=[1, 1]
            )

        mocked.assert_called_once_with(1, 1)

    def test_sheet_name_must_be_truncated_if_larger_than_31_chars(self):
        """Test that the sheet name must be truncated if larger than 31 chars"""

        exporter = ExcelDataExporter()

        sheet_name = 'This string is too long to match the maximum length of the excel sheet name'

        exporter.add_sheet(
            sheet_name,
            self.data_frame,
            [EXCEL_STYLE_CHOICES.DEFAULT],
            freeze_panes=[1, 1]
        )

        self.assertIsNotNone(
            exporter.workbook.get_worksheet_by_name(
                Truncator(sheet_name).chars(31, '…')
            )
        )
