"""ECP Auth session middleware.

Manages ECP-related session data, including challenge nonce
lifecycle and certificate information attached to requests.
"""

from __future__ import annotations

import logging
from typing import Any, Callable

from django.http import HttpRequest, HttpResponse

from ecp_auth.crypto import Challenge
from ecp_auth.utils import get_from_session, remove_from_session

logger: logging.Logger = logging.getLogger(__name__)


class ECPSessionMiddleware:
    """Middleware for managing ECP authentication session data.

    Attaches ECP-related information to the request object and
    cleans up expired challenge nonces from the session.

    Usage:
        Add to ``MIDDLEWARE`` in settings::

            MIDDLEWARE = [
                ...,
                'ecp_auth.middleware.ECPSessionMiddleware',
            ]
    """

    def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]) -> None:
        """Initialize the middleware.

        Args:
            get_response: The next middleware or view callable.
        """
        self.get_response = get_response

    def __call__(self, request: HttpRequest) -> HttpResponse:
        """Process the request.

        Attaches ECP session data to the request and cleans up
        expired challenges before passing to the next handler.

        Args:
            request: The incoming HTTP request.

        Returns:
            The HTTP response from the next handler.
        """
        # Attach ECP info to request
        self._attach_ecp_info(request)

        # Clean up expired challenges
        self._cleanup_expired_challenges(request)

        response: HttpResponse = self.get_response(request)

        return response

    def _attach_ecp_info(self, request: HttpRequest) -> None:
        """Attach ECP-related information to the request object.

        Sets ``request.ecp_authenticated`` and ``request.ecp_certificate_cn``
        attributes based on the current session state.

        Args:
            request: The HTTP request to augment.
        """
        request.ecp_authenticated = False  # type: ignore[attr-defined]
        request.ecp_certificate_cn = None  # type: ignore[attr-defined]

        cert_fingerprint: str | None = get_from_session(
            request, "certificate_fingerprint"
        )

        if cert_fingerprint and hasattr(request, "user") and request.user.is_authenticated:
            request.ecp_authenticated = True  # type: ignore[attr-defined]
            request.ecp_certificate_cn = get_from_session(  # type: ignore[attr-defined]
                request, "certificate_cn"
            )

            logger.debug(
                "ECP session active for user %s (cert CN: %s).",
                request.user.username,
                request.ecp_certificate_cn,  # type: ignore[attr-defined]
            )

    def _cleanup_expired_challenges(self, request: HttpRequest) -> None:
        """Remove expired challenge nonces from the session.

        Args:
            request: The HTTP request with session.
        """
        challenge_data: dict[str, Any] | None = get_from_session(
            request, "challenge"
        )

        if challenge_data is None:
            return

        try:
            challenge = Challenge.from_dict(challenge_data)
            if challenge.is_expired:
                remove_from_session(request, "challenge")
                logger.debug("Cleaned up expired challenge from session.")
        except Exception:
            # If challenge data is corrupted, remove it
            remove_from_session(request, "challenge")
            logger.warning("Removed corrupted challenge data from session.")
