"""This module contains the data exporters for the ``questionnaires`` app"""

import pandas as pd

from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _

from mtp.apps.data_export.constants import EXCEL_STYLE_CHOICES
from mtp.apps.data_export.exporters import ExcelDataExporter
from mtp.apps.trainings.models import ResultsToExcelActivityMixin


class QuestionnaireResultsToExcel(ResultsToExcelActivityMixin):
    """Result to excel class for the ``Questionnaire`` subclasses"""

    @cached_property
    def is_scoring(self):
        """Return the scoring status of the current ``Questionnaire`` instance

        Returns
        -------
        bool
            True if the current ``Questionnaire`` is a subclass of ``WithScoreMixin``,
            False otherwise

        """

        from mtp.apps.questionnaires.models import WithScoreMixin

        return isinstance(self, WithScoreMixin)

    def results_to_excel(self, session, path=None):
        """Get the session results of this questionnaire as an excel file

        For the parameters, see ``ResultsToExcelActivityMixin``

        """

        excel_exporter = ExcelDataExporter(path)

        # Add the sheet for the global scores (in the case of a scoring ``Questionnaire``)
        # (data obtained with `get_results_excel_data` with the detailed parameter to `False`)
        if self.is_scoring:
            excel_data = self.get_results_excel_data(session, detailed=False)
            excel_data['sheet_name'] = '%s - %s' % (
                _('Global scores'),
                self.name
            )
            excel_exporter.add_sheet(**excel_data)

        # Add the sheet for the detailed scores
        # (data obtained with the `get_results_excel_data` with the detailed parameter to `True`)
        excel_detailed_data = self.get_results_excel_data(session, detailed=True)
        excel_detailed_data['sheet_name'] = '%s - %s' % (
            _('Detailed answers'),
            self.name
        )
        excel_exporter.add_sheet(**excel_detailed_data)

        return excel_exporter.save()

    def get_results_excel_data(self, session, detailed=False):
        """Get the results data of this questionnaire for a given session

        Parameters
        ----------
        session: TrainingSession
            The training session we want the results for
        detailed: bool
            If False, only the score will be added to the output,
            If True, the verbatim (the details of the trainee answers for each question)
            will also be also be added to the output.

        Returns
        -------
        dict
            A dict containing the parameters for the ``ExcelDataExporter.add_sheet``

        """

        questions = self.questions.non_deleted()
        users = session.users.non_deleted()

        series_list = []
        header_styles = []

        for index, question in enumerate(questions, start=1):
            # Build the series for this question, containing the scores for each trainees
            question_series = pd.Series(
                [
                    question.get_trainee_session_score(user, session) if self.is_scoring else ''
                    for user in users
                ],
                name='%d - %s' % (
                    index,
                    question.label
                )
            )

            # Append the max score available for this step to the series
            if self.is_scoring:
                question_series[len(users)] = question.points

            # Store the series and the styles for the header to apply to it
            series_list.append(question_series)
            header_styles.append(EXCEL_STYLE_CHOICES.WORKSHOP_STEP)

            # If the "detailed" flag was provided, store also the details of this steps
            # and the styles to apply to them
            if detailed:
                print(question)
                # pylint: disable=bad-builtin
                detailed_series, detailed_styles = map(
                    list, zip(*question.get_results_excel_data(session))
                )
                series_list.extend(detailed_series)
                header_styles.extend(detailed_styles)

        def build_data_frame(series):
            """Build a data frame for the given list of series

            Parameters
            ----------
            series: list(pandas.Series)
                The series to build the data frame from

            Returns
            -------
            pandas.DataFrame

            """

            data_frame = pd.concat(series, axis=1)

            # Pop the "Maximum points yield" index
            if self.is_scoring:
                max_points = list(data_frame.loc[len(users)].values)
                data_frame = data_frame[:-1]

                # Add a "Mean" row containing the mean value for each columns
                data_frame.loc[str(_('Mean'))] = data_frame.mean(numeric_only=True)

                # Round the all the numeric values in the data frame
                data_frame = data_frame.round(2)

            # Finally, populate the indexes using the users last and first name (username)
            data_frame = data_frame.rename(
                index={
                    index: '%(last_name)s %(first_name)s (%(username)s)' % {
                        'last_name': user.last_name,
                        'first_name': user.first_name,
                        'username': user.username
                    }
                    for index, user in enumerate(users)
                }
            )

            if self.is_scoring:
                # Re-add the "Maximum points yield" row
                data_frame.loc[str(_('Maximum points'))] = max_points

                # Add the "Total" column at the beginning
                data_frame.insert(0, str(_('Total')), data_frame.sum(axis=1))

            return data_frame

        if self.is_scoring:
            # Prepend the styles for the "Total" column
            header_styles = [EXCEL_STYLE_CHOICES.HEADER] + header_styles

        return {
            'sheet_name': str(_('Global scores')),
            'data': build_data_frame(series_list),
            'header_styles': header_styles,
            'freeze_panes': [1, 1]
        }

