import logging

from django.http import HttpResponse, StreamingHttpResponse, QueryDict
from rest_framework import permissions, exceptions
from rest_framework.parsers import BaseParser
from rest_framework.views import APIView, Response

from .decorators import validate_sns_message_signature, determine_staff
from .serializers import APISerializer

logger = logging.getLogger('ddyxdebug')


class PlainTextOrJsonParser(BaseParser):
    """
    Plain text parser.
    """
    media_type = 'text/plain'

    def parse(self, stream, media_type=None, parser_context=None):
        """
        Attempt to parse the string as JSON failing which return a string representing the body of the request.
        Written to handle messages from AWS SNS, but may be needed for TCS inbound notifications
        """
        import json
        raw = stream.read()
        try:
            data = json.loads(raw)
        except:
            data = raw.decode('utf-8')

        return data


class ActionAPIView(APIView):
    """
    Base class for action services taken by the new front end.
    The action function will be determined, if it exists and called.
    Each action function takes the request, expecting JSON body (if required) and returns a dictionary
    or Response instance
    """
    permission_classes = [permissions.IsAuthenticated]

    _last_action = None
    success = True
    RESPONSE_KEYS = ['user', 'action', 'token', 'subject', 'type', 'messageid']
    raw_body = ''

    def post(self, request, action, **kwargs):
        return self.get(request, action, **kwargs)

    @determine_staff
    def get(self, request, action, **kwargs):
        params = self.normalize_params(request)
        kwargs['params'] = params
        self._last_action = params.get('action', action)
        try:
            lv_action = self.__getattribute__(self._last_action)
        except AttributeError:
            lv_action = self.action_does_not_exist
        response = lv_action(request, **kwargs)
        if isinstance(response, Response):
            response = response.data
        if isinstance(response, (HttpResponse, StreamingHttpResponse)):
            return response

        response_context = dict(
            [item for item in list({
                k: params.get(k, None) for k in self.RESPONSE_KEYS
            }.items()) if item[1] is not None])
        serialised = APISerializer(response, context=response_context)
        return Response(serialised.data)

    def action_does_not_exist(self, *args, **kwargs):
        return {'success': False, 'reason': "Action '%s' does not exist." % self._last_action}

    def normalize_params(self, request):
        """
        Combines the possible parameter sources from the request.
        :param request: a REST API request object
        :return: a combined dict of the 2 possible data sources from the request
        """
        try:
            self.raw_body = request._request.body
        except:
            self.raw_body = ""
        params = request.query_params.dict().copy()
        if isinstance(request.data, QueryDict):
            params.update(request.data.dict())
        else:
            params.update(request.data)
        return params

    def check_roles(self, valid_roles, section='this task'):
        if self.staff.roles.filter(id__in=valid_roles).count() == 0:
            raise exceptions.PermissionDenied('You do not have sufficient right to perform %s.' % section)


class SNSActionAPIView(ActionAPIView):
    permission_classes = []
    parser_classes = [PlainTextOrJsonParser]

    def get(self, request, action, **kwargs):
        return super().get(request, action, **kwargs)


class SnsMessageActionAPIViewset(SNSActionAPIView):
    ''' Acts as an example of how to implement a self confirming SNS endpoint '''
    @validate_sns_message_signature
    def echo(self, request, params, *args, **kwargs):
        logger.info(params)
        return params

    @validate_sns_message_signature
    def test_registration(self, request, params, *args, **kwargs):
        logger.info(params)

        return params