"""
ErrorCaptureMiddleware - Automatically capture backend exceptions.

This middleware intercepts unhandled exceptions in Django and logs them
to the ApplicationError model for display in the admin error dashboard.
"""

import logging

from django.http import HttpRequest, HttpResponse

from apps.core.services import ErrorService

logger = logging.getLogger(__name__)


class ErrorCaptureMiddleware:
    """
    Middleware that captures unhandled exceptions and stores them.

    This runs after Django's built-in exception handling but before
    the exception is raised to the user, allowing us to log all errors
    that occur during request processing.

    Usage:
        Add to settings.MIDDLEWARE after SecurityMiddleware but before
        any middleware that might swallow exceptions:

        MIDDLEWARE = [
            'django.middleware.security.SecurityMiddleware',
            'apps.core.middleware.error_capture.ErrorCaptureMiddleware',
            ...
        ]
    """

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request: HttpRequest) -> HttpResponse:
        response = self.get_response(request)

        # Capture 5xx errors that made it through (server errors)
        if response.status_code >= 500:
            self._capture_response_error(request, response)

        return response

    def process_exception(self, request: HttpRequest, exception: Exception) -> None:
        """
        Capture exception details before Django handles it.

        This method is called when a view raises an exception.
        It logs the error to the database but returns None to allow
        Django to continue with its default exception handling.

        Args:
            request: The HTTP request that caused the exception
            exception: The exception that was raised

        Returns:
            None - let Django continue with default handling
        """
        try:
            self._capture_exception(request, exception)
        except Exception as e:
            # Don't let error capture failures break the request
            logger.error(f"Failed to capture exception: {e}")

        return None  # Let Django continue with default handling

    def _capture_exception(
        self, request: HttpRequest, exception: Exception
    ) -> None:
        """Capture an exception to the database."""
        # Determine severity based on exception type
        severity = self._classify_severity(exception)

        # Determine source based on request path
        source = "api" if "/api/" in request.path else "backend"

        ErrorService.capture_error(
            error=exception,
            source=source,
            request=request,
            severity=severity,
            metadata={
                "view": self._get_view_name(request),
                "query_params": dict(request.GET),
            },
        )

    def _capture_response_error(
        self, request: HttpRequest, response: HttpResponse
    ) -> None:
        """Capture a 5xx response error to the database."""
        # Only capture if we have a reason phrase or content
        reason = getattr(response, "reason_phrase", "Server Error")
        content = ""
        if hasattr(response, "content"):
            try:
                content = response.content.decode("utf-8")[:500]
            except (UnicodeDecodeError, AttributeError):
                pass

        error_message = f"HTTP {response.status_code}: {reason}"
        if content:
            error_message += f" - {content[:200]}"

        severity = "critical" if response.status_code >= 503 else "high"
        source = "api" if "/api/" in request.path else "backend"

        ErrorService.capture_error(
            error=error_message,
            source=source,
            request=request,
            severity=severity,
            metadata={
                "status_code": response.status_code,
                "reason_phrase": reason,
                "view": self._get_view_name(request),
            },
        )

    def _classify_severity(self, exception: Exception) -> str:
        """Classify exception severity based on type."""
        exception_type = type(exception).__name__

        # Critical: Database, system, and security errors
        critical_types = {
            "DatabaseError",
            "OperationalError",
            "IntegrityError",
            "PermissionError",
            "SystemError",
            "MemoryError",
        }

        # High: Unexpected runtime errors
        high_types = {
            "RuntimeError",
            "TypeError",
            "ValueError",
            "AttributeError",
            "KeyError",
            "IndexError",
        }

        # Medium: Expected application errors
        medium_types = {
            "ValidationError",
            "Http404",
            "NotFound",
            "PermissionDenied",
            "AuthenticationFailed",
        }

        if exception_type in critical_types:
            return "critical"
        elif exception_type in high_types:
            return "high"
        elif exception_type in medium_types:
            return "medium"
        else:
            return "high"  # Default to high for unknown errors

    def _get_view_name(self, request: HttpRequest) -> str:
        """Get the name of the view that handled the request."""
        if hasattr(request, "resolver_match") and request.resolver_match:
            return request.resolver_match.view_name or ""
        return ""
