"""This module contains the apiv2 related views for the ``media`` app"""

import os

from django.conf import settings
from django.http import Http404
from django.shortcuts import get_object_or_404
from django.utils import formats
from django.utils.functional import cached_property

from rest_framework import filters, viewsets
from rest_framework.views import APIView

from mtp.apps.api.permissions import IsAuthenticated
from mtp.apps.api.renderers import TemplatePDFRenderer
from mtp.apps.pdf_reporting.views import PDFReportMixin
from mtp.apps.trainings.models import TrainingSession

from ..models import (
    Note,
    Presentation,
    Video,
)

from .serializers import (
    NoteSerializer,
    PresentationSerializer,
    VideoSerializer,
)


class PresentationViewSet(viewsets.ReadOnlyModelViewSet):
    """ViewSet for the ``Presentation`` model"""

    permission_classes = [IsAuthenticated]
    serializer_class = PresentationSerializer
    queryset = Presentation.objects.non_deleted()


class VideoViewSet(viewsets.ReadOnlyModelViewSet):
    """ViewSet for the ``Video`` model"""

    permission_classes = [IsAuthenticated]
    serializer_class = VideoSerializer
    queryset = Video.objects.non_deleted()


class BaseNoteViewSet(viewsets.ModelViewSet):
    """Base ViewSet for the ``Note`` model"""

    filter_backends = (filters.DjangoFilterBackend,)
    filter_fields = (
        'training_session',
    )
    permission_classes = [IsAuthenticated]
    serializer_class = NoteSerializer

    def get_queryset(self):
        """Filter the notes the user can see

        Returns
        -------
        NoteQuerySet

        """

        return Note.objects.filter(user=self.request.user)


class FreeNoteViewSet(BaseNoteViewSet):
    """ViewSet for the ``Note``model  not linked to a ``Presentation``"""

    def get_queryset(self):
        """Exclude the notes linked to a ``Presentation``

        Returns
        -------
        NoteQuerySet

        """

        return super().get_queryset().without_presentation()


class PresentationNoteViewSet(BaseNoteViewSet):
    """ViewSet for the ``Note``model linked to given ``Presentation``"""

    @cached_property
    def presentation(self):
        """Get the presentation from the url parameter

        Returns
        -------
        presentation: Presentation

        Raises
        ------
        Http404
            If the presentation cannot be found

        """

        return get_object_or_404(
            Presentation.objects.non_deleted(),
            id=self.kwargs['presentation_uuid']
        )

    def get_queryset(self):
        """Exclude the notes linked to a ``Presentation``

        Returns
        -------
        NoteQuerySet

        """

        return super().get_queryset().for_presentation(self.presentation)


class AnnotatedPresentationPdfDownloadView(APIView, PDFReportMixin):
    """API View to download an annotated presentation as a pdf file"""

    renderer_classes = [TemplatePDFRenderer]
    permission_classes = [IsAuthenticated]
    template_name = 'rml/presentation_with_notes.rml'

    @cached_property
    def session(self):
        """Get the session from the url

        Returns
        -------
        TrainingSession

        Raises
        ------
        Http404
            If the session is not found.

        """

        return get_object_or_404(
            TrainingSession.objects.non_deleted(),
            pk=self.kwargs['session_uuid']
        )

    @cached_property
    def presentation(self):
        """Get the presentation from the url parameter

        Returns
        -------
        presentation: Presentation

        Raises
        ------
        Http404
            If the presentation cannot be found

        """

        return get_object_or_404(
            Presentation.objects.non_deleted(),
            id=self.kwargs['presentation_uuid']
        )

    def get_filename(self):
        """Get the pdf filename

        Returns
        -------
        str

        """

        return "%(presentation)s_%(short_date)s_%(user)s" % {
            'presentation': self.presentation.name,
            'short_date': formats.date_format(self.session.start_date, "SHORT_DATE_FORMAT"),
            'user': self.request.user
        }

    def get_context_data(self, **kwargs):
        """Add the presentation, the presentation images and user's notes to the context

        For the parameters, see ``django.forms.generic.base.ContextMixin``

        Raises
        ------
        Http404
            If no user notes or no images exists for the current presentation

        """

        notes = list(
            Note.objects.for_presentation(self.presentation).filter(
                training_session=self.session,
                user=self.request.user
            ).non_deleted()
        )

        # We don't have any notes for the given session / user / presentation
        # or the presentation don't have any generated images.
        if not notes or not self.presentation.pdf.images_url_list:
            raise Http404

        def get_image_absolute_path(image_relative_path):
            """Shortcut method to get the pdf image absolute path

            - Remove the "/media/" from the file path, as the "pdf_images_url_list"
            - Add the ``MEDIA_ROOT`` to have the absolute path

            Parameters
            ----------
            image_relative_path: str
                The relative path of the image url.

            Returns
            -------
            str

            """

            return os.path.abspath(os.path.join(
                settings.MEDIA_ROOT,
                '/'.join(image_relative_path.split('/')[2:])
            ))

        images_with_notes = [
            {
                # Remove the "/media/" from the file path, as the "pdf_images_url_list"
                # from above return the image paths including the "/media/" part and we
                # are adding the `MEDIA_ROOT` to the path in the template.
                'image': get_image_absolute_path(image_url),
                'notes': [
                    note.content
                    for note in notes
                    if note.presentation_page == page_index
                ]
            }
            for page_index, image_url in enumerate(self.presentation.pdf.images_url_list, start=1)
        ]

        return super().get_context_data(
            images_with_notes=images_with_notes,
            presentation=self.presentation,
            session=self.session,
            **kwargs
        )

    def get(self, request, *args, **kwargs):  # pylint: disable=unused-argument
        """Render the response

        For the parameters, see ``rest_framework.views.APIView``

        """

        context = self.get_context_data(**kwargs)

        return self.render_to_response(context)

