"""This module holds the views of the ``api`` app."""

import datetime
import json
import os
from subprocess import call

from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.decorators import user_passes_test
from django.http import HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
from django.views.generic import View

from rest_framework.views import APIView

from mtp.apps.accounts.models import MTPUser
from mtp.apps.assessments.models import CompetencyTree
from mtp.apps.common.utils import handle_uploaded_file
from mtp.apps.common.views_utils import CrsfExemptMixin
from mtp.apps.themes.models import Theme
from mtp.apps.trainings.models import TrainingSession

from .constants import BAD_REQUEST_HTTP_RESPONSE, EMPTY_HTTP_RESPONSE


class ClientSettings(CrsfExemptMixin, View):
    """View to get the "settings" for the client"""

    def post(self, request, *args, **kwargs):  # pylint: disable=unused-argument
        """Build and return a dict of settings for the client

        Returns
        -------
        JsonResponse
            The client settings as a json response

        """

        try:
            ipa_version = float(request.POST['app_version'])
        except (KeyError, ValueError):
            return BAD_REQUEST_HTTP_RESPONSE

        default_theme = Theme.objects.non_deleted().filter(default=True).first()

        client_settings = {
            'server': settings.CURRENT_INSTANCE_HOST,
            'server_mode': settings.INSTANCE_IS_SLAVE and 'local' or 'saas',
            'api_path': 'api',
            'download_server': settings.CURRENT_INSTANCE_URL,
            'version': ipa_version,
            'need_app_update': True,
            'client_id': settings.CLIENT_NAME,
            'url_message_broker': settings.MESSAGE_BROKER_URL,
            'port_message_broker': settings.MESSAGE_BROKER_PORT,
            'default_theme_id': default_theme.pk if default_theme else None,
            'app_update': settings.LAST_IPA_VERSION_ITMS_URL,
        }

        return JsonResponse(
            {'settings': client_settings}
        )


def downloads(request, file_id):
    """Check if a given file can be safely downloaded

    The check was made to avoid overloading the local server with multiple file
    download requests at once (sort of a "download queue" I guess ?).
    Obviously, this is not working at the moment.

    Returns
    -------
    ``django.http.HttpResponse``
        An empty response

    Notes
    -----
    This endpoint is actually used by the iOS client.

    """

    return EMPTY_HTTP_RESPONSE


@user_passes_test(MTPUser.test_if_superuser_or_trainer)
def stop_server(request):
    """Shutdown the server

    Returns
    -------
    ``django.http.HttpResponse``
        An empty response

    Notes
    -----
    This is only working on the ``LOCAL`` servers

    """

    if settings.INSTANCE_IS_SLAVE:
        call(['sudo', 'poweroff'])

    return EMPTY_HTTP_RESPONSE


@require_POST
@csrf_exempt
def ping(request):
    """Mark user as still alive, either in session or in the waiting room.

    See the documentation of `MTPUser.keep_alive` for implementation details.

    Returns
    -------
    ``django.http.HttpResponse``
        An empty response

    """

    user_klass = get_user_model()
    user_id = request.POST.get('user_id')
    session_id = request.POST.get('session_id')

    try:
        user = user_klass.objects.get(pk=user_id)
    except user_klass.DoesNotExist:
        return EMPTY_HTTP_RESPONSE

    user.keep_alive(session_id=session_id)

    return EMPTY_HTTP_RESPONSE


@csrf_exempt
def upload_file(request):
    """Upload a file into the provided folder

    Returns
    -------
    ``django.http.HttpResponse``
        An empty response if the upload is successful,
        A bad response (400) if unsuccessful

    """

    if request.method == 'POST':
        folder = os.path.join(settings.UPLOAD_ROOT, request.POST['folder'])

        if not os.path.exists(folder):
            os.makedirs(folder)

        filename = os.path.join(folder, request.POST['filename'])
        handle_uploaded_file(request.FILES['file'], filename)

        return EMPTY_HTTP_RESPONSE
    return BAD_REQUEST_HTTP_RESPONSE


@csrf_exempt
def upload_post(request, folder_name):
    """Upload multiple file

    A folder is created with the current timestamp,
    and the submitted files are moved into it.

    Returns
    -------
    ``django.http.HttpResponse``
        An json response with the folder name if successful,
        A bad response (400) if unsuccessful

    """

    if request.method == "POST":
        folder = os.path.join(settings.UPLOAD_ROOT, folder_name)
        timestamp = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
        folder = os.path.join(folder, timestamp)

        if not os.path.exists(folder):
            os.makedirs(folder)

        for key in request.POST:
            value = request.POST[key]
            filename = os.path.join(folder, key)
            f = open(filename, "w")
            f.write(value)
            f.close()

        for key in request.FILES:
            filename = os.path.join(folder, key)
            handle_uploaded_file(request.FILES[key], filename)

        response = {"timestamp": timestamp}
        return HttpResponse(json.dumps(response),
                            content_type="application/json")

    return BAD_REQUEST_HTTP_RESPONSE


# Assessments views

class AssessmentDebrief(APIView):
    """
    Return the necessary data to build the assessment debrief app view.

    Example
    -------
    The data is returned in that format:

    >>> {
    >>>     "score": 75,
    >>>
    >>>     "graphs": [
    >>>         {
    >>>             "title": "Competencies map",
    >>>             "data": [
    >>>                 {
    >>>                     "name": "competency-1",
    >>>                     "description": "description competency-1",
    >>>                     "score": 50
    >>>                 },
    >>>                 {
    >>>                     "name": "competency-2",
    >>>                     "description": "description competency-2",
    >>>                     "score": 50
    >>>                 },
    >>>                 {
    >>>                     "name": "competency-3",
    >>>                     "description": "description competency-3",
    >>>                     "score": 50
    >>>                 }
    >>>             ]
    >>>         },
    >>>         {
    >>>             "title": "Dimensions map for Competency 1",
    >>>             "data": [
    >>>                 {
    >>>                     "name": "dimension-1-1",
    >>>                     "description": "description dimension-1-1",
    >>>                     "score": 50
    >>>                 },
    >>>                 {
    >>>                     "name": "dimension-1-2",
    >>>                     "description": "description dimension-1-2",
    >>>                     "score": 50
    >>>                 },
    >>>                 {
    >>>                     "name": "dimension-1-3",
    >>>                     "description": "description dimension-1-3",
    >>>                     "score": 50
    >>>                 },
    >>>             ]
    >>>         },
    >>>
    >>>         # other dimensions...?
    >>>
    >>>     ],
    >>>
    >>>     "activities": [
    >>>         {
    >>>             "type": "roleplay",
    >>>             "name": "toto",
    >>>             "description": "blablabla",
    >>>             "steps": [
    >>>                 {
    >>>                     "name": "toto-step1",
    >>>                     "description": "description toto-step1",
    >>>                     "criteria": [
    >>>                         {
    >>>                             "type": "QL",
    >>>                             "name": "toto-step1-crit1",
    >>>                             "description": "description toto-step1-crit1",
    >>>                             "text": "fuck yeah",
    >>>                             "score": -1,
    >>>                         },
    >>>                         {
    >>>                             "type": "QL",
    >>>                             "name": "toto-step1-crit2",
    >>>                             "description": "description toto-step1-crit2",
    >>>                             "text": "fuck yeah",
    >>>                             "score": -1,
    >>>                         },
    >>>                         {
    >>>                             "type": "QL",
    >>>                             "name": "toto-step1-crit3",
    >>>                             "description": "description toto-step1-crit3",
    >>>                             "text": "fuck yeah",
    >>>                             "score": -1,
    >>>                         },
    >>>                     ]
    >>>                 },
    >>>
    >>>                 # other steps...?
    >>>
    >>>             ]
    >>>         },
    >>>
    >>>         # other activities...?
    >>>
    >>>     ],
    >>>
    >>>     "suggestions": []
    >>> }

    See also
    --------
    ``mtp.apps.assessments.models.competencies.CompetencyTree.compute_debrief_data``

    """

    def get(self, request, trainee_id, session_id):

        trainee = get_object_or_404(MTPUser, pk=trainee_id)
        session = get_object_or_404(TrainingSession, pk=session_id)

        # If the session is still going on, the scores may fluctuate
        # from one call to the next. In order to maintain freshness, we
        # recompute them each time. Upon closing the session, they will be
        # recomputed for the last, final time.
        if session.state != TrainingSession.STATE_CHOICES.CLOSED:
            CompetencyTree.compute_scores(trainee, session, force_recompute=True)

        # We should now be able to compute the debrief data.
        try:
            debrief_data = CompetencyTree.compute_debrief_data(trainee, session)
        except RuntimeError:
            return BAD_REQUEST_HTTP_RESPONSE

        return JsonResponse(debrief_data)
