"""
Request logging middleware for comprehensive request/response logging.
Logs all HTTP requests with detailed data for debugging and monitoring.
"""

import json
import logging
import time

from django.utils.deprecation import MiddlewareMixin

logger = logging.getLogger("request_logging")


class RequestLoggingMiddleware(MiddlewareMixin):
    """
    Middleware to log all HTTP requests with method, path, and response code.
    Includes detailed request/response data logging for all requests.
    """

    # Paths to exclude from detailed logging (e.g., static files, health checks)
    EXCLUDE_DETAILED_LOGGING_PATHS = [
        "/static/",
        "/media/",
        "/favicon.ico",
        "/health/",
        "/admin/jsi18n/",
    ]

    def _should_log_detailed(self, request):
        """Determine if detailed logging should be enabled for this request."""
        return not any(path in request.path for path in self.EXCLUDE_DETAILED_LOGGING_PATHS)

    def process_request(self, request):
        """Store request start time and capture request data for detailed logging."""
        request._start_time = time.time()

        # Enable detailed logging for all requests except excluded paths
        should_log_detailed = self._should_log_detailed(request)
        request._log_request_data = should_log_detailed

        if should_log_detailed:
            try:
                # Log request data
                request_data = {}
                if hasattr(request, "data") and request.data:
                    request_data = dict(request.data)
                elif request.body:
                    try:
                        request_data = json.loads(request.body.decode("utf-8"))
                    except (json.JSONDecodeError, UnicodeDecodeError):
                        request_data = {
                            "body": (
                                str(request.body)[:200] + "..." if len(str(request.body)) > 200 else str(request.body)
                            )
                        }

                # Log query parameters
                query_params = dict(request.GET) if request.GET else {}

                logger.info(f"REQUEST DATA for {request.method} {request.path}:")
                if request_data:
                    logger.info(f"  Body: {self._safe_log_data(request_data)}")
                if query_params:
                    logger.info(f"  Query: {query_params}")
                if hasattr(request, "user") and request.user.is_authenticated:
                    logger.info(f"  User: {request.user.username} (ID: {request.user.id})")

            except Exception as e:
                logger.warning(f"Failed to log request data: {e}")

        return None

    def process_response(self, request, response):
        """Log request details after response is generated."""
        try:
            # Calculate request duration
            duration = 0
            if hasattr(request, "_start_time"):
                duration = time.time() - request._start_time

            # Basic request logging
            logger.info(f"{request.method} {request.get_full_path()} -> {response.status_code} " f"({duration:.3f}s)")

            # Detailed response logging for specific endpoints
            if getattr(request, "_log_request_data", False):
                try:
                    # Log response data
                    if hasattr(response, "data"):
                        logger.info(f"RESPONSE DATA for {request.method} {request.path}:")
                        logger.info(f"  Status: {response.status_code}")
                        logger.info(f"  Data: {self._safe_log_data(response.data)}")
                    elif hasattr(response, "content"):
                        try:
                            content = json.loads(response.content.decode("utf-8"))
                            logger.info(f"RESPONSE DATA for {request.method} {request.path}:")
                            logger.info(f"  Status: {response.status_code}")
                            logger.info(f"  Content: {self._safe_log_data(content)}")
                        except (json.JSONDecodeError, UnicodeDecodeError):
                            logger.info(f"RESPONSE DATA for {request.method} {request.path}:")
                            logger.info(f"  Status: {response.status_code}")
                            logger.info(f"  Content: {str(response.content)[:200]}...")

                except Exception as e:
                    logger.warning(f"Failed to log response data: {e}")

        except Exception:
            # Don't let logging errors break the request
            pass

        return response

    # Sensitive field patterns that should be masked in logs
    # Security: Comprehensive list of sensitive data patterns
    SENSITIVE_PATTERNS = [
        "password",
        "passwd",
        "pwd",
        "token",
        "secret",
        "key",
        "api_key",
        "apikey",
        "auth",
        "authorization",
        "credential",
        "ssn",
        "social_security",
        "credit_card",
        "creditcard",
        "card_number",
        "cvv",
        "cvc",
        "pin",
        "access_token",
        "refresh_token",
        "jwt",
        "session",
        "cookie",
        "private",
    ]

    def _safe_log_data(self, data):
        """
        Safely log data, truncating if too large and masking sensitive fields.

        Security measures:
        - Masks all sensitive field names
        - Masks email addresses (shows only domain)
        - Truncates long values to prevent log flooding
        - Recursively processes nested dictionaries and lists
        """
        try:
            if isinstance(data, dict):
                safe_data = self._mask_sensitive_dict(data)
                data_str = json.dumps(safe_data, indent=2, default=str)
            elif isinstance(data, list):
                safe_data = [self._mask_sensitive_value(item) for item in data]
                data_str = json.dumps(safe_data, indent=2, default=str)
            else:
                data_str = json.dumps(data, indent=2, default=str)

            # Truncate if too long
            if len(data_str) > 1000:
                return data_str[:1000] + "...[TRUNCATED]"
            return data_str
        except Exception:
            return str(data)[:500] + "...[ERROR_LOGGING]"

    def _mask_sensitive_dict(self, data, depth=0):
        """Recursively mask sensitive fields in a dictionary."""
        if depth > 5:  # Prevent infinite recursion
            return "***DEPTH_LIMIT***"

        safe_data = {}
        for key, value in data.items():
            key_lower = str(key).lower()

            # Check if key contains any sensitive pattern
            if any(pattern in key_lower for pattern in self.SENSITIVE_PATTERNS):
                safe_data[key] = "***MASKED***"
            else:
                safe_data[key] = self._mask_sensitive_value(value, depth)

        return safe_data

    def _mask_sensitive_value(self, value, depth=0):
        """Mask a single value, handling different types."""
        if isinstance(value, dict):
            return self._mask_sensitive_dict(value, depth + 1)
        elif isinstance(value, list):
            return [self._mask_sensitive_value(item, depth + 1) for item in value[:10]]  # Limit list items
        elif isinstance(value, str):
            # Mask email addresses (show only domain)
            if "@" in value and "." in value.split("@")[-1]:
                parts = value.split("@")
                if len(parts) == 2:
                    return f"***@{parts[1]}"
            # Truncate long strings
            if len(value) > 200:
                return value[:200] + "...[TRUNCATED]"
        return value
