"""
Custom ASGI application for mTLS support.

This module provides a custom ASGI application that properly handles
client certificates in mTLS connections.
"""

import logging
from typing import Dict, Any, Optional
from starlette.types import ASGIApp, Receive, Send, Scope

logger = logging.getLogger(__name__)


class MTLSASGIApp:
    """
    Custom ASGI application that properly handles mTLS client certificates.

    This wrapper ensures that client certificates are properly extracted
    and made available to the FastAPI application.
    """

    def __init__(self, app: ASGIApp, ssl_config: Dict[str, Any]):
        """
        Initialize MTLS ASGI application.

        Args:
            app: The underlying ASGI application (FastAPI)
            ssl_config: SSL configuration for mTLS
        """
        self.app = app
        self.ssl_config = ssl_config
        self.verify_client = ssl_config.get("verify_client", False)
        self.client_cert_required = ssl_config.get("client_cert_required", False)

        get_global_logger().info(
            f"MTLS ASGI app initialized: verify_client={self.verify_client}, "
            f"client_cert_required={self.client_cert_required}"
        )

    async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
        """
        Handle ASGI request with mTLS support.

        Args:
            scope: ASGI scope
            receive: ASGI receive callable
            send: ASGI send callable
        """
        try:
            # Extract client certificate from SSL context
            if scope["type"] == "http" and "ssl" in scope:
                client_cert = self._extract_client_certificate(scope)
                if client_cert:
                    # Store certificate in scope for middleware access
                    scope["client_certificate"] = client_cert
                    get_global_logger().debug(
                        f"Client certificate extracted: {client_cert.get('subject', {})}"
                    )
                elif self.client_cert_required:
                    get_global_logger().warning("Client certificate required but not provided")
                    # Return 401 Unauthorized
                    await self._send_unauthorized_response(send)
                    return

            # Call the underlying application
            await self.app(scope, receive, send)

        except Exception as e:
            get_global_logger().error(f"Error in MTLS ASGI app: {e}")
            await self._send_error_response(send, str(e))

    def _extract_client_certificate(self, scope: Scope) -> Optional[Dict[str, Any]]:
        """
        Extract client certificate from SSL context.

        Args:
            scope: ASGI scope

        Returns:
            Client certificate data or None
        """
        try:
            ssl_context = scope.get("ssl")
            if not ssl_context:
                return None

            # Get peer certificate
            cert = ssl_context.getpeercert()
            if cert:
                return cert

            return None

        except Exception as e:
            get_global_logger().error(f"Failed to extract client certificate: {e}")
            return None

    async def _send_unauthorized_response(self, send: Send) -> None:
        """
        Send 401 Unauthorized response.

        Args:
            send: ASGI send callable
        """
        response = {
            "type": "http.response.start",
            "status": 401,
            "headers": [
                (b"content-type", b"application/json"),
                (b"content-length", b"163"),
            ],
        }
        await send(response)

        body = b'{"jsonrpc": "2.0", "error": {"code": -32001, "message": "Unauthorized: Client certificate required"}, "id": null}'
        await send({"type": "http.response.body", "body": body})

    async def _send_error_response(self, send: Send, error_message: str) -> None:
        """
        Send error response.

        Args:
            send: ASGI send callable
            error_message: Error message
        """
        response = {
            "type": "http.response.start",
            "status": 500,
            "headers": [
                (b"content-type", b"application/json"),
            ],
        }
        await send(response)

        body = f'{{"jsonrpc": "2.0", "error": {{"code": -32603, "message": "Internal error: {error_message}"}}, "id": null}}'.encode()
        await send({"type": "http.response.body", "body": body})


