"""
Security Headers Middleware for ThrillWiki.

This middleware adds additional security headers to all HTTP responses,
providing defense-in-depth against common web vulnerabilities.

Headers added:
- Content-Security-Policy: Controls resource loading to prevent XSS
- Permissions-Policy: Restricts browser feature access
- Cross-Origin-Embedder-Policy: Prevents cross-origin embedding
- Cross-Origin-Resource-Policy: Restricts cross-origin resource access

Usage:
    Add 'apps.core.middleware.security_headers.SecurityHeadersMiddleware'
    to MIDDLEWARE in settings.py (after SecurityMiddleware).
"""

import logging

from django.conf import settings

logger = logging.getLogger(__name__)


class SecurityHeadersMiddleware:
    """
    Middleware that adds security headers to HTTP responses.

    This provides defense-in-depth by adding headers that Django's
    SecurityMiddleware doesn't handle.
    """

    def __init__(self, get_response):
        self.get_response = get_response
        # Build CSP header at startup for performance
        self._csp_header = self._build_csp_header()
        self._permissions_policy_header = self._build_permissions_policy_header()

    def __call__(self, request):
        response = self.get_response(request)
        return self._add_security_headers(response, request)

    def _add_security_headers(self, response, request):
        """Add security headers to the response."""
        # Content-Security-Policy
        # Only add CSP for HTML responses to avoid breaking API/JSON responses
        content_type = response.get("Content-Type", "")
        if "text/html" in content_type:
            if not response.get("Content-Security-Policy"):
                response["Content-Security-Policy"] = self._csp_header
            else:
                logger.warning(f"CSP header already present for {request.path}, skipping")

        # Permissions-Policy (successor to Feature-Policy)
        if not response.get("Permissions-Policy"):
            response["Permissions-Policy"] = self._permissions_policy_header

        # Cross-Origin-Embedder-Policy
        # Requires resources to be CORS-enabled or same-origin
        # Using 'unsafe-none' for now as 'require-corp' can break third-party resources
        if not response.get("Cross-Origin-Embedder-Policy"):
            response["Cross-Origin-Embedder-Policy"] = "unsafe-none"

        # Cross-Origin-Resource-Policy
        # Controls how resources can be shared with other origins
        if not response.get("Cross-Origin-Resource-Policy"):
            response["Cross-Origin-Resource-Policy"] = "same-origin"

        logger.debug(f"Added security headers to response for {request.path}")

        return response

    def _build_csp_header(self):
        """
        Build the Content-Security-Policy header value.

        CSP directives explained:
        - default-src: Fallback for other fetch directives
        - script-src: Sources for JavaScript
        - style-src: Sources for CSS
        - img-src: Sources for images
        - font-src: Sources for fonts
        - connect-src: Sources for fetch, XHR, WebSocket
        - frame-ancestors: Controls framing (replaces X-Frame-Options)
        - form-action: Valid targets for form submissions
        - base-uri: Restricts base element URLs
        - object-src: Sources for plugins (Flash, etc.)
        """
        # Check if we're in debug mode
        debug = getattr(settings, "DEBUG", False)

        # Base directives (production-focused)
        directives = {
            "default-src": ["'self'"],
            "script-src": [
                "'self'",
                # Allow HTMX inline scripts with nonce (would need nonce middleware)
                # For now, using 'unsafe-inline' for HTMX compatibility
                "'unsafe-inline'" if debug else "'self'",
                # CDNs for external scripts
                "https://cdn.jsdelivr.net",
                "https://unpkg.com",
                "https://challenges.cloudflare.com",  # Turnstile
            ],
            "style-src": [
                "'self'",
                "'unsafe-inline'",  # Required for Tailwind and inline styles
                "https://cdn.jsdelivr.net",
                "https://fonts.googleapis.com",
            ],
            "img-src": [
                "'self'",
                "data:",
                "blob:",
                "https:",  # Allow HTTPS images (needed for user uploads, maps, etc.)
            ],
            "font-src": [
                "'self'",
                "https://fonts.gstatic.com",
                "https://cdn.jsdelivr.net",
            ],
            "connect-src": [
                "'self'",
                "https://api.forwardemail.net",
                "https://challenges.cloudflare.com",
                "https://*.cloudflare.com",
                # Map tile servers
                "https://*.openstreetmap.org",
                "https://*.tile.openstreetmap.org",
            ],
            "frame-src": [
                "'self'",
                "https://challenges.cloudflare.com",  # Turnstile widget
            ],
            "frame-ancestors": ["'self'"],
            "form-action": ["'self'"],
            "base-uri": ["'self'"],
            "object-src": ["'none'"],
            "upgrade-insecure-requests": [],  # Upgrade HTTP to HTTPS
        }

        # Add debug-specific relaxations
        if debug:
            # Allow webpack dev server connections in development
            directives["connect-src"].extend(
                [
                    "ws://localhost:*",
                    "http://localhost:*",
                    "http://127.0.0.1:*",
                ]
            )

        # Build header string
        parts = []
        for directive, sources in directives.items():
            if sources:
                parts.append(f"{directive} {' '.join(sources)}")
            else:
                # Directives like upgrade-insecure-requests don't need values
                parts.append(directive)

        return "; ".join(parts)

    def _build_permissions_policy_header(self):
        """
        Build the Permissions-Policy header value.

        This header controls which browser features the page can use.
        """
        # Get permissions policy from settings or use defaults
        policy = getattr(
            settings,
            "PERMISSIONS_POLICY",
            {
                "accelerometer": [],
                "ambient-light-sensor": [],
                "autoplay": [],
                "camera": [],
                "display-capture": [],
                "document-domain": [],
                "encrypted-media": [],
                "fullscreen": ["self"],
                "geolocation": ["self"],
                "gyroscope": [],
                "interest-cohort": [],
                "magnetometer": [],
                "microphone": [],
                "midi": [],
                "payment": [],
                "picture-in-picture": [],
                "publickey-credentials-get": [],
                "screen-wake-lock": [],
                "sync-xhr": [],
                "usb": [],
                "web-share": ["self"],
                "xr-spatial-tracking": [],
            },
        )

        parts = []
        for feature, allowlist in policy.items():
            if not allowlist:
                # Empty list means disallow completely
                parts.append(f"{feature}=()")
            else:
                # Convert allowlist to proper format
                formatted = " ".join(allowlist)
                parts.append(f"{feature}=({formatted})")

        return ", ".join(parts)
