"""Request ID middleware for structured logging context.

Generates or extracts a request ID for every incoming request and binds it
(along with tenant_id from JWT) to structlog's contextvars so that all log
lines emitted during the request include these correlation fields.
"""

import uuid

import structlog
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import Response


class RequestIDMiddleware(BaseHTTPMiddleware):
    """Middleware that binds request_id and tenant_id to structlog context."""

    async def dispatch(self, request: Request, call_next):
        # CRITICAL: Clear stale context from previous request to prevent leaks
        structlog.contextvars.clear_contextvars()

        # Generate or extract request ID
        request_id = request.headers.get("X-Request-ID", str(uuid.uuid4()))

        # Best-effort tenant_id extraction from JWT (does not fail if no token)
        tenant_id = self._extract_tenant_id(request)

        # Bind to structlog context -- all subsequent log calls include these fields
        structlog.contextvars.bind_contextvars(
            request_id=request_id,
            tenant_id=tenant_id,
        )

        response: Response = await call_next(request)
        response.headers["X-Request-ID"] = request_id
        return response

    def _extract_tenant_id(self, request: Request) -> str | None:
        """Best-effort extraction of tenant_id from JWT.

        Looks in cookies first (access_token), then Authorization header.
        Returns None if no valid token is found -- this is fine for
        unauthenticated endpoints like /login.
        """
        token = request.cookies.get("access_token")
        if not token:
            auth_header = request.headers.get("Authorization", "")
            if auth_header.startswith("Bearer "):
                token = auth_header[7:]

        if not token:
            return None

        try:
            from jose import jwt as jose_jwt

            from app.config import settings

            payload = jose_jwt.decode(
                token,
                settings.JWT_SECRET_KEY,
                algorithms=[settings.JWT_ALGORITHM],
            )
            return payload.get("tenant_id")
        except Exception:
            return None
