from rest_framework import serializers

from django.core.exceptions import PermissionDenied
from django.http import QueryDict
from django.utils import timezone
from django.utils.module_loading import import_string

from ....conf import settings


class PostingInterrupt(Exception):
    def __init__(self, message):  # pylint: disable=super-init-not-called
        if not message:
            raise ValueError("You have to provide PostingInterrupt message.")
        self.message = message


class PostingEndpoint:
    START = 0
    REPLY = 1
    EDIT = 2

    def __init__(self, request, mode, **kwargs):
        # we are using lock on user model to protect us from flood
        request.user.lock()

        # build kwargs dict for passing to middlewares
        self.kwargs = kwargs
        self.kwargs.update(
            {
                "mode": mode,
                "request": request,
                "settings": request.settings,
                "user": request.user,
                "user_acl": request.user_acl,
            }
        )

        self.__dict__.update(kwargs)

        # some middlewares (eg. emailnotification) may call render()
        # which will crash if this isn't set to false
        request.include_frontend_context = False

        self.datetime = timezone.now()
        self.errors = {}
        self._is_validated = False

        self.middlewares = self._load_middlewares()
        self._serializers = self._initialize_serializers()

    @property
    def is_start_endpoint(self):
        return self.mode == self.START

    @property
    def is_reply_endpoint(self):
        return self.mode == self.REPLY

    @property
    def is_edit_endpoint(self):
        return self.mode == self.EDIT

    def _load_middlewares(self):
        kwargs = self.kwargs.copy()
        kwargs.update({"datetime": self.datetime, "parsing_result": {}})

        middlewares = []
        for middleware in settings.MISAGO_POSTING_MIDDLEWARES:
            middleware_class = import_string(middleware)

            try:
                middleware_obj = middleware_class(prefix=middleware, **kwargs)
                if middleware_obj.use_this_middleware():
                    middlewares.append((middleware, middleware_obj))
            except PostingInterrupt:
                raise ValueError(
                    "Posting process can only be interrupted during pre_save phase"
                )

        return middlewares

    def get_serializers(self):
        """return list of serializers belonging to serializerset"""
        return self._serializers

    def _initialize_serializers(self):
        try:
            serializers = {}
            for middleware, obj in self.middlewares:
                serializer = obj.get_serializer()
                if serializer:
                    serializers[middleware] = serializer
            return serializers
        except PostingInterrupt:
            raise ValueError(
                "Posting process can only be interrupted during pre_save phase"
            )

    def is_valid(self):
        """validate data against all serializers"""
        for serializer in self._serializers.values():
            if not serializer.is_valid():
                self.errors.update(serializer.errors)

        self._is_validated = True
        return not self.errors

    def save(self):
        """save new state to backend"""
        if not self._is_validated or self.errors:
            raise RuntimeError(
                "You need to validate posting data successfully before calling save"
            )

        try:
            for middleware, obj in self.middlewares:
                obj.pre_save(self._serializers.get(middleware))
        except PostingInterrupt as e:
            raise ValueError(
                "Posting process can only be interrupted "
                "from within interrupt_posting method"
            )

        try:
            for middleware, obj in self.middlewares:
                obj.interrupt_posting(self._serializers.get(middleware))
        except PostingInterrupt as e:
            raise PermissionDenied(e.message)

        try:
            for middleware, obj in self.middlewares:
                obj.save(self._serializers.get(middleware))
            for middleware, obj in self.middlewares:
                obj.post_save(self._serializers.get(middleware))
        except PostingInterrupt as e:
            raise ValueError(
                "Posting process can only be interrupted "
                "from within interrupt_posting method"
            )


class PostingMiddleware:
    """abstract middleware class"""

    def __init__(self, **kwargs):
        self.kwargs = kwargs
        self.__dict__.update(kwargs)

    def use_this_middleware(self):
        return True

    def get_serializer(self):
        pass

    def pre_save(self, serializer):
        pass

    def interrupt_posting(self, serializer):
        pass

    def save(self, serializer):
        pass

    def post_save(self, serializer):
        pass
