# coding: utf-8

import logging
from collections import OrderedDict
from urllib.parse import quote

from django.conf import settings
from django.http import HttpResponse
from django.utils.encoding import force_bytes
from magic import Magic
from rest_framework import status
from rest_framework.exceptions import PermissionDenied, ValidationError
from rest_framework.parsers import MultiPartParser
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

from procu.api import models
from procu.api.auth import get_username_by_uid
from procu.api.html.views import (
    maybe_redirect_to_login,
    maybe_redirect_to_passport,
    maybe_redirect_to_resign,
    maybe_remove_url_params,
)
from procu.api.utils import is_internal
from procu.rest import generics
from procu.rest.authentication import MixedAuthentication
from .permissions import FileRetrievePermission
from .serializers import (
    AttachmentEntrySerializer,
    AttachmentSerializer,
    DocViewerQuery,
)

logger = logging.getLogger(__name__)


class AttachmentFile(generics.GenericAPIView):

    parser_classes = (MultiPartParser,)
    serializer_class = AttachmentSerializer

    def post(self, request):

        try:
            request.data['file']

        except KeyError:
            raise ValidationError({'file': 'File is required'})

        # Check authentication and permissions later
        # so that the request is read from the client first.
        # Otherwise the error may not be displayed by the client.

        self.authentication_classes = (MixedAuthentication,)
        self.perform_authentication(request)

        self.permission_classes = (IsAuthenticated,)
        self.check_permissions(request)

        # ----------------------------------------------------------------------

        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        filename = serializer.validated_data['file'].name

        serializer.save(filename=filename, author=self.request.user)

        return Response(serializer.data, status=status.HTTP_201_CREATED)

    def meta_post(self, components):

        info = OrderedDict()

        if 'in' in components:
            write_schema = self.get_schema(write=True)

            parameters = []

            for field_name, field in write_schema['properties'].items():
                parameter = OrderedDict([('name', field_name), ('in', 'form')])

                if field is not None:
                    parameter.update(field)

                parameters.append(parameter)

            info['parameters'] = parameters

        if 'out' in components:
            info['responses'] = {'default': self.get_schema(write=False)}

        return info


def get_file_response(attachment):

    magic = Magic(mime=True)
    mimetype = magic.from_buffer(attachment.file.read(4098))

    file_response = HttpResponse(attachment.file, content_type=mimetype)

    encoded_filename = quote(force_bytes(attachment.filename))

    file_response['Content-Disposition'] = (
        "inline; filename*=UTF-8''%s" % encoded_filename
    )

    return file_response


class AttachmentFileRetrieve(generics.RetrieveAPIView):
    permission_classes = ()
    serializer_class = AttachmentEntrySerializer
    queryset = models.Attachment.objects.all()
    lookup_url_kwarg = 'attachment_id'

    def retrieve(self, request, *args, **kwargs):

        # ----------------------------------------------------------------------

        user = request.user

        # Instantiate lazy user-object
        is_authenticated = user.is_authenticated

        if is_internal(request):
            if is_authenticated:
                response = maybe_redirect_to_resign(request)
                if response is not None:
                    return response

            else:
                response = maybe_redirect_to_passport(request)
                if response is not None:
                    return response

        else:
            response = maybe_remove_url_params(request)
            if response is not None:
                return response

            if not is_authenticated:
                response = maybe_redirect_to_login(request)
                if response is not None:
                    return response

        # ----------------------------------------------------------------------

        self.permission_classes = (FileRetrievePermission,)
        self.check_permissions(request)

        # ----------------------------------------------------------------------

        instance = self.get_object()

        magic = Magic(mime=True)
        mimetype = magic.from_buffer(instance.file.read(4098))

        file_response = HttpResponse(instance.file, content_type=mimetype)

        encoded_filename = quote(force_bytes(instance.filename))

        file_response['Content-Disposition'] = (
            "attachment; filename*=UTF-8''%s" % encoded_filename
        )

        return file_response


def always_permission_denied(exc, context):
    return Response(
        {'detail': 'Permission denied'}, status=status.HTTP_403_FORBIDDEN
    )


class DocViewerView(generics.RetrieveAPIView):
    query_params_serializer_class = DocViewerQuery
    serializer_class = AttachmentEntrySerializer
    queryset = models.Attachment.objects.all()

    def get_exception_handler(self):
        return always_permission_denied

    def get(self, request, *args, **kwargs):
        uid = self.query_params.get('uid')
        attachment = self.query_params.get('attachment')
        username = None

        if request.yauser and request.yauser.authenticated_by.mechanism_name == 'tvm':
            if request.yauser.service_ticket.src != settings.TVM_DOCVIEWER_CLIENT_ID:
                raise PermissionDenied('Prohibited service')
            username = request.yauser.username

        user = generics.get_object_or_404(
            queryset=models.User.objects.all(),
            username=username or get_username_by_uid(uid),
        )

        attach_id, filename = attachment.split('/', 1)
        instance = models.Attachment.objects.get(id=attach_id)
        if instance.file.name != filename:
            raise PermissionDenied('Permission denied')

        self.request.user = user

        perm = FileRetrievePermission()

        if not (
            perm.has_permission(request, self)
            and perm.has_object_permission(request, self, instance)
        ):
            raise PermissionDenied('Permission denied')

        # ----------------------------------------------------------------------

        return get_file_response(instance)
