"""This module holds the api-related views of the ``surveys`` app"""

import logging
from itertools import groupby
import json
from operator import itemgetter

from django.http import JsonResponse
from django.shortcuts import get_object_or_404
from django.views.generic.detail import BaseDetailView

from mtp.apps.api.constants import EMPTY_HTTP_RESPONSE
from mtp.apps.common.views_utils import RestrictToTraineeMixin
from mtp.apps.trainings.models import TrainingSession

from ..models import Answer, Survey

logger = logging.getLogger('mtp.api')


class SessionSurvey(RestrictToTraineeMixin, BaseDetailView):

    http_method_names = ['get', 'head', 'post']

    def get_object(self, queryset=None):

        # Get the session.
        self.session = get_object_or_404(TrainingSession, pk=self.kwargs['session_uuid'])

        # Then get (and update) or create the survey.
        survey, __ = Survey.objects.get_or_create_for_training(
            self.session.training,
            self.session.trainer
        )

        # The survey will be ``self.object`` in the view
        return survey

    def render_to_response(self, context, **response_kwargs):  # pylint: disable=unused-argument
        """
        Returns a JSON response, transforming 'context' to make the payload.

        For the parameters, see ``django.views.generic.View``
        """
        return JsonResponse(
            self.object.serialize(),
            **response_kwargs
        )

    def post(self, request, *args, **kwargs):  # pylint: disable=unused-argument
        """Saving of survey, build in a way to be  compatible with data posted from a workshop.

        The data is expected to be JSON formatted in the ``json`` entry of the post data
        (form-encoded format).
        The answers are in a list in a ``data`` entry in thi JSON.
        Each answer contains:
        - ``question_id``: the id of the question for which the answer is,
        - ``answer_id``: the id of the selected *choice* (we use "answer" to be compatible with
                         the workshop api), for a single/multi choices question,
        - ``text``: the text for an open question.

        For the parameters, see ``django.views.generic.View``

        Example
        -------

        Data is expected to be in this format:

        >>> data = [
        >>>     { # question simple choice
        >>>         'question_id': 'q1',
        >>>         'answer_id': 'q1a1',
        >>>         'text': '',
        >>>     },
        >>>     { # question multiple choices, first choice
        >>>         'question_id': 'q2',
        >>>         'answer_id': 'q2a1',
        >>>         'text': '',
        >>>     },
        >>>     { # question multiple choices, second choice
        >>>         'question_id': 'q2',
        >>>         'answer_id': 'q2a2',
        >>>         'text': '',
        >>>     },
        >>>     { # open question
        >>>         'question_id': 'q3',
        >>>         'answer_id': '',
        >>>         'text': 'some text',
        >>>     }
        >>> ]

        Notes
        -----

        The given data is then transformed to be grouped by ``question_id``.

        >>> grouped_data = [
        >>>     { # question simple choice
        >>>         'question_id': 'q1',
        >>>         'answer_ids': [
        >>>             'q1a1',
        >>>         ],
        >>>         'text': '',
        >>>     },
        >>>     { # question multiple choices with all choices
        >>>         'question_id': 'q2',
        >>>         'answer_ids': [
        >>>             'q2a1',
        >>>             'q2a2',
        >>>         ],
        >>>         'text': '',
        >>>     },
        >>>     { # open question
        >>>         'question_id': 'q3',
        >>>         'answer_ids': []
        >>>         'text': 'some text',
        >>>     },
        >>> ]

        """

        self.object = self.get_object()

        # Take data from the input that is json with a ``data`` entry.
        data = json.loads(request.POST['json'])['data']

        # Regroup by question_id to manage multiple choices.
        # See in the ``Notes`` section of the docstring to see expected output.

        grouped_data = [
            {
                'question_id': question_id,
                'answer_ids': [
                    answer['answer_id']
                    for answer in answers if answer['answer_id']
                ],
                'text': answers[0]['text'],
            }
            for question_id, answers in [
                (question_id, list(answers_generator))  # needed because we'll use...
                for question_id, answers_generator in groupby(  # ...the generator twice.
                    sorted(  # we need to sort before the group by.
                        data,
                        key=itemgetter('question_id')
                    ),
                    key=itemgetter('question_id')
                )
            ]
        ]

        # Save each answer independently. If an answer cannot be saved, just log the error and
        # go to the next question.
        for answer_data in grouped_data:

            try:
                Answer.objects.save_answer(
                    survey=self.object,
                    session=self.session,
                    trainee=self.request.user,
                    question_pk=answer_data['question_id'],
                    choice_pks=answer_data.get('answer_ids'),
                    text=answer_data.get('text'),
                )

            except Exception as exc:  # pylint: disable=broad-except
                # We ignore problems with an answer, it's not expected by the mobile application.
                # But we can still log it.
                logger.exception(
                    'Error saving survey answer.'
                    'code=[%(error_code)s]'
                    'trainee=[%(user_id)s] '
                    'session=[%(session_id)s] '
                    'survey=[%(survey_id)s] '
                    'question=[%(question_id)s] '
                    'choices=[%(choice_ids)s] '
                    'text filled=[%(text_filled)s]',
                    {
                        'error_code': getattr(exc, 'code', None),
                        'user_id': self.request.user.id,
                        'session_id': self.session.id,
                        'survey_id': self.object.id,
                        'question_id': answer_data['question_id'],
                        'choice_ids': answer_data.get('answer_ids'),
                        'text_filled': bool(answer_data.get('text')),
                    }
                )

        return EMPTY_HTTP_RESPONSE
