import contextlib

from django.conf import settings
from django.test.client import (
    MULTIPART_CONTENT,
    Client,
)
from django.urls import reverse
from rest_framework import permissions

from vq_django_auth.app_settings import api_settings


class CustomPermissionClass(permissions.BasePermission):
    message = "You shall not pass!"

    def has_permission(self, request, view):
        return False


class APIClient(Client):
    def patch(
        self, path, data="", content_type=MULTIPART_CONTENT, follow=False, **extra
    ):
        return self.generic("PATCH", path, data, content_type, **extra)

    def options(
        self, path, data="", content_type=MULTIPART_CONTENT, follow=False, **extra
    ):
        return self.generic("OPTIONS", path, data, content_type, **extra)


class TestsMixin:
    """
    Base mixin for API tests that provides helpful testing utilities.

    Features:
    - Simplified HTTP request methods (get, post, patch)
    - Automatic status code validation
    - JSON response parsing
    - Authentication header handling for token auth
    - Common test initialization

    Usage:
        class YourTestClass(TestsMixin):
            def test_something(self):
                # Initialize the test environment
                self.init()

                # Make authenticated requests
                self.post(self.login_url, data=payload, status_code=200)
                self.token = self.response.json["key"]  # Store token for auth

                # Make requests that expect the token to be included
                self.get(self.user_url, status_code=200)
    """

    def send_request(self, request_method, *args, **kwargs):
        """
        Send an HTTP request and process the response.

        Args:
            request_method: The HTTP method function to call (e.g. 'get', 'post')
            *args: Arguments to pass to the request method
            **kwargs: Keyword arguments including:
                - data: The request payload (will be JSON-encoded for non-GET requests)
                - status_code: Expected status code for assertion
                - content_type: Content type header (defaults to 'application/json')

        Returns:
            Response object with additional .json attribute containing parsed response

        Note:
            If self.token is set, Authorization header will be automatically added
        """
        request_func = getattr(self.client, request_method)

        self.response = request_func(*args, **kwargs)

        return self.response

    def post(self, *args, **kwargs):
        return self.send_request("post", *args, **kwargs)

    def get(self, *args, **kwargs):
        return self.send_request("get", *args, **kwargs)

    def patch(self, *args, **kwargs):
        return self.send_request("patch", *args, **kwargs)

    def init(self):
        settings.DEBUG = True
        self.client = APIClient()

        self.login_url = reverse("rest_login")
        self.logout_url = reverse("rest_logout")
        self.password_change_url = reverse("rest_password_change")
        self.password_reset_url = reverse("rest_password_reset")
        self.password_reset_confirm = reverse("rest_password_reset_confirm")
        self.user_url = reverse("rest_user_details")


@contextlib.contextmanager
def override_api_settings(**settings):
    old_settings = {}

    for k, v in settings.items():
        # Save settings
        try:
            old_settings[k] = api_settings.user_settings[k]
        except KeyError:
            pass

        # Install temporary settings
        api_settings.user_settings[k] = v

        # Delete any cached settings
        try:
            delattr(api_settings, k)
        except AttributeError:
            pass

    try:
        yield
    finally:
        for k in settings.keys():
            # Delete temporary settings
            api_settings.user_settings.pop(k)

            # Restore saved settings
            try:
                api_settings.user_settings[k] = old_settings[k]
            except KeyError:
                pass

            # Delete any cached settings
            try:
                delattr(api_settings, k)
            except AttributeError:
                pass
