"""
Django middleware for payload-guard.
Provides request/response filtering for Django applications.
"""

from __future__ import annotations
import json
from typing import Any, Callable, Optional
from functools import wraps

from django.http import HttpRequest, HttpResponse, JsonResponse
from django.conf import settings

from ..core.filter import build_shape, GuardConfig
from ..core.types import ShapeDescriptor, GuardMiddlewareOptions
from ..core.compiler import CompileOptions, compile_shape
from ..core.security import DEFAULT_SENSITIVE_FIELDS


class PayloadGuardMiddleware:
    """
    Django middleware for payload filtering and sanitization.

    Example:
        # settings.py
        MIDDLEWARE = [
            # ...
            'payloadguard.middleware.django.PayloadGuardMiddleware',
        ]

        PAYLOAD_GUARD_CONFIG = {
            'request_shape': {
                'name': 'string',
                'email': 'string',
            },
            'dev_mode': True,
        }
    """

    def __init__(
        self,
        get_response: Callable[[HttpRequest], HttpResponse],
        request_shape: Optional[ShapeDescriptor] = None,
        response_shape: Optional[ShapeDescriptor] = None,
        sensitive_fields: Optional[list[str]] = None,
        dev_mode: bool = False,
        strict: bool = False,
        max_array_length: Optional[int] = None,
        collect_errors: bool = False,
        ignore_routes: Optional[list[str]] = None,
        sanitize_body: bool = True,
        filter_response: bool = False,
        **kwargs,
    ):
        self.get_response = get_response
        self.request_shape = request_shape
        self.response_shape = response_shape
        self.sensitive_fields = sensitive_fields or []
        self.dev_mode = dev_mode
        self.strict = strict
        self.max_array_length = max_array_length
        self.collect_errors = collect_errors
        self.ignore_routes = ignore_routes or []
        self.sanitize_body = sanitize_body
        self.filter_response = filter_response
        self.extra_config = kwargs

        # Load config from Django settings if available
        if hasattr(settings, "PAYLOAD_GUARD_CONFIG"):
            config = settings.PAYLOAD_GUARD_CONFIG
            if request_shape is None:
                self.request_shape = config.get("request_shape")
            if response_shape is None:
                self.response_shape = config.get("response_shape")
            if dev_mode is False:
                self.dev_mode = config.get("dev_mode", False)
            if strict is False:
                self.strict = config.get("strict", False)
            if sensitive_fields is None:
                self.sensitive_fields = config.get("sensitive_fields", [])
            if max_array_length is None:
                self.max_array_length = config.get("max_array_length")
            if collect_errors is False:
                self.collect_errors = config.get("collect_errors", False)
            if ignore_routes is None:
                self.ignore_routes = config.get("ignore_routes", [])

        # Compile shapes
        self._request_filter = None
        self._response_filter = None

        if self.request_shape:
            self._request_filter = compile_shape(
                self.request_shape,
                CompileOptions(
                    sensitive=self.sensitive_fields + DEFAULT_SENSITIVE_FIELDS,
                    dev=self.dev_mode,
                    strict=self.strict,
                    max_array_length=self.max_array_length,
                    collect_errors=self.collect_errors,
                ),
            )

        if self.response_shape:
            self._response_filter = compile_shape(
                self.response_shape,
                CompileOptions(
                    sensitive=self.sensitive_fields + DEFAULT_SENSITIVE_FIELDS,
                    dev=self.dev_mode,
                    strict=self.strict,
                    max_array_length=self.max_array_length,
                    collect_errors=self.collect_errors,
                ),
            )

    def _should_ignore(self, path: str) -> bool:
        """Check if route should be ignored."""
        for prefix in self.ignore_routes:
            if path.startswith(prefix):
                return True
        return False

    def __call__(self, request: HttpRequest) -> HttpResponse:
        """Process request and response."""
        # Check if route should be ignored
        if self._should_ignore(request.path):
            return self.get_response(request)

        # Sanitize request body
        if self.sanitize_body and self._request_filter and request.method in ("POST", "PUT", "PATCH"):
            try:
                if request.content_type == "application/json":
                    body = json.loads(request.body)
                    sanitized_body = self._request_filter(body)
                    request._sanitized_body = sanitized_body
                    # Update request body
                    request._body = json.dumps(sanitized_body).encode()
            except Exception as e:
                if self.dev_mode:
                    print(f"[payload-guard] Request sanitization error: {e}")

        # Get response
        response = self.get_response(request)

        # Filter response
        if self.filter_response and self._response_filter and isinstance(response, JsonResponse):
            try:
                # Get response data
                content = response.content.decode()
                data = json.loads(content)

                # Filter data
                filtered_data = self._response_filter(data)

                # Create new response
                return JsonResponse(filtered_data, status=response.status_code)
            except Exception as e:
                if self.dev_mode:
                    print(f"[payload-guard] Response filtering error: {e}")

        return response


def guard_middleware(
    request_shape: Optional[ShapeDescriptor] = None,
    response_shape: Optional[ShapeDescriptor] = None,
    options: Optional[GuardMiddlewareOptions] = None,
) -> type[PayloadGuardMiddleware]:
    """
    Create payload guard middleware for Django.

    Args:
        request_shape: Shape for request body sanitization
        response_shape: Shape for response filtering
        options: Middleware options

    Returns:
        Middleware class for Django settings.MIDDLEWARE
    """
    if options is None:
        options = GuardMiddlewareOptions()

    class DjangoGuardMiddleware(PayloadGuardMiddleware):
        def __init__(self, get_response: Callable, **kwargs):
            super().__init__(
                get_response,
                request_shape=request_shape or options.request_shape,
                response_shape=response_shape or options.response_shape,
                sensitive_fields=options.sensitive_fields,
                dev_mode=options.dev_mode or False,
                strict=options.strict or False,
                max_array_length=options.max_array_length,
                collect_errors=options.collect_errors or False,
                sanitize_body=options.sanitize_body,
                filter_response=options.filter_response,
                **kwargs,
            )

    return DjangoGuardMiddleware


def route_guard(
    shape: ShapeDescriptor,
    sensitive_fields: Optional[list[str]] = None,
    dev_mode: bool = False,
    strict: bool = False,
) -> Callable:
    """
    Decorator for view-level payload validation.

    Example:
        @require_http_methods(["POST"])
        @route_guard(user_shape)
        def create_user(request):
            data = request._sanitized_body
            return JsonResponse(data)
    """

    def decorator(func: Callable) -> Callable:
        @wraps(func)
        def wrapper(request: HttpRequest, *args, **kwargs) -> HttpResponse:
            # Compile shape
            request_filter = compile_shape(
                shape,
                CompileOptions(
                    sensitive=(sensitive_fields or []) + DEFAULT_SENSITIVE_FIELDS,
                    dev=dev_mode,
                    strict=strict,
                ),
            )

            # Sanitize request body
            if request.content_type == "application/json" and request.method in ("POST", "PUT", "PATCH"):
                try:
                    body = json.loads(request.body)
                    sanitized = request_filter(body)
                    request._sanitized_body = sanitized
                except Exception as e:
                    if dev_mode:
                        print(f"[payload-guard] Route guard error: {e}")

            return func(request, *args, **kwargs)

        return wrapper

    return decorator


def get_sanitized_body(request: HttpRequest) -> Optional[dict]:
    """Get sanitized request body."""
    return getattr(request, "_sanitized_body", None)
