#
# Copyright (c) 2018, Grigoriy Kramarenko
# All rights reserved.
# This file is distributed under the BSD 3-Clause License.
#
"""
Вспомогательный функционал для обработки запросов.
"""
import json
from functools import wraps
from io import BytesIO

from django.http.request import QueryDict, MultiValueDict
from django.http.multipartparser import MultiPartParserError
from django.utils.encoding import force_text
from django.utils.decorators import available_attrs
from django.views.decorators.cache import cache_page


def _user_checksum(user):
    if hasattr(user, 'checksum'):
        return user.checksum
    return str(user.pk)


def cache_user_page(timeout):
    """
    Декоратор кэшированных представлений с разделением на пользователей.
    """

    def decorator(view_func):
        @wraps(view_func, assigned=available_attrs(view_func))
        def _wrapped_view(request, *args, **kwargs):
            user = request.user
            if user.is_authenticated:
                key_prefix = "_auth_%s_" % _user_checksum(user)
            else:
                key_prefix = "_auth_0_"
            view = cache_page(timeout, key_prefix=key_prefix)(view_func)
            return view(request, *args, **kwargs)
        return _wrapped_view
    return decorator


def _file_parser(request):
    if hasattr(request, '_body'):
        # Use already read data
        data = BytesIO(request._body)
    else:
        data = request
    try:
        _data, _files = request.parse_file_upload(request.META, data)
    except MultiPartParserError:
        # An error occurred while parsing POST data. Since when
        # formatting the error the request handler might access
        # request.POST, set request._post and request._file to prevent
        # attempts to parse POST data again.
        # Mark that an error occurred. This allows request.__repr__ to
        # be explicit about it instead of simply representing an
        # empty POST
        request._mark_post_parse_error()
        raise
    return _data, _files


def _form_parser(request):
    data = QueryDict(request.body, encoding=request._encoding)
    return data, MultiValueDict()


def _json_parser(request):
    try:
        data = json.loads(
            force_text(request.body, encoding=request._encoding or 'utf-8') or
            '{}'
        )
        request.json_data_ready = True
    except ValueError:
        request._mark_post_parse_error()
        raise
    return data, MultiValueDict()


def _blank_parser(request):
    data = QueryDict('', encoding=request._encoding)
    return data, MultiValueDict()


def _get_parser(request):
    content_type = request.META.get('CONTENT_TYPE', '')
    if content_type.startswith('multipart/form-data'):
        return _file_parser
    elif content_type.startswith('application/x-www-form-urlencoded'):
        return _form_parser
    elif content_type.startswith('application/json'):
        return _json_parser
    return _blank_parser


def parse_data(function=None):
    """Декоратор для представлений, использующихся в качестве REST API."""

    def decorator(view_func):
        @wraps(view_func)
        def _wrapped_view(request, *args, **kwargs):
            request.json_data_ready = False
            if not hasattr(request, 'data'):
                if request.method == 'GET':
                    request.data = request.GET
                else:
                    if request._read_started and not hasattr(request, '_body'):
                        request._mark_post_parse_error()
                    else:
                        parser = _get_parser(request)
                        request.data, request._files = parser(request)
            return view_func(request, *args, **kwargs)
        return _wrapped_view
    if function:
        return decorator(function)
    return decorator
