"""Audit middleware"""
from functools import partial
from typing import Callable

from django.contrib.auth.models import User
from django.db.models import Model
from django.db.models.signals import post_save, pre_delete
from django.http import HttpRequest, HttpResponse

from passbook.audit.models import Event, EventAction, model_to_dict
from passbook.audit.signals import EventNewThread
from passbook.core.middleware import LOCAL


class AuditMiddleware:
    """Register handlers for duration of request-response that log creation/update/deletion
    of models"""

    get_response: Callable[[HttpRequest], HttpResponse]

    def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]):
        self.get_response = get_response

    def __call__(self, request: HttpRequest) -> HttpResponse:
        # Connect signal for automatic logging
        if hasattr(request, "user") and getattr(
            request.user, "is_authenticated", False
        ):
            post_save_handler = partial(
                self.post_save_handler, user=request.user, request=request
            )
            pre_delete_handler = partial(
                self.pre_delete_handler, user=request.user, request=request
            )
            post_save.connect(
                post_save_handler,
                dispatch_uid=LOCAL.passbook["request_id"],
                weak=False,
            )
            pre_delete.connect(
                pre_delete_handler,
                dispatch_uid=LOCAL.passbook["request_id"],
                weak=False,
            )

        response = self.get_response(request)

        post_save.disconnect(dispatch_uid=LOCAL.passbook["request_id"])
        pre_delete.disconnect(dispatch_uid=LOCAL.passbook["request_id"])

        return response

    # pylint: disable=unused-argument
    def process_exception(self, request: HttpRequest, exception: Exception):
        """Unregister handlers in case of exception"""
        post_save.disconnect(dispatch_uid=LOCAL.passbook["request_id"])
        pre_delete.disconnect(dispatch_uid=LOCAL.passbook["request_id"])

    @staticmethod
    # pylint: disable=unused-argument
    def post_save_handler(
        user: User, request: HttpRequest, sender, instance: Model, created: bool, **_
    ):
        """Signal handler for all object's post_save"""
        if isinstance(instance, Event):
            return

        action = EventAction.MODEL_CREATED if created else EventAction.MODEL_UPDATED
        EventNewThread(action, request, user=user, model=model_to_dict(instance)).run()

    @staticmethod
    # pylint: disable=unused-argument
    def pre_delete_handler(
        user: User, request: HttpRequest, sender, instance: Model, **_
    ):
        """Signal handler for all object's pre_delete"""
        if isinstance(instance, Event):
            return

        EventNewThread(
            EventAction.MODEL_DELETED,
            request,
            user=user,
            model=model_to_dict(instance),
        ).run()