"""
Sentry middleware for FastAPI to add request context and performance monitoring.
"""

import time
from collections.abc import Callable

import sentry_sdk
from fastapi import Request, Response
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.types import ASGIApp

from app.core.logging import get_logger

logger = get_logger("sentry_middleware")


class SentryMiddleware(BaseHTTPMiddleware):
    """Middleware to add Sentry context and performance monitoring."""

    def __init__(self, app: ASGIApp):
        super().__init__(app)

    async def dispatch(self, request: Request, call_next: Callable) -> Response:
        """Process request and add Sentry context."""
        # Start timing
        start_time = time.time()

        # Set request context in Sentry
        with sentry_sdk.start_transaction(
            op="http.server",
            name=f"{request.method} {request.url.path}",
            description=f"{request.method} {request.url.path}",
        ) as transaction:
            # Set transaction context
            transaction.set_data("http.method", request.method)
            transaction.set_data("http.url", str(request.url))
            transaction.set_data("http.query_string", request.url.query)

            # Set request headers context (excluding sensitive ones)
            headers = dict(request.headers)
            sensitive_headers = {"authorization", "cookie", "x-api-key"}
            safe_headers = {
                k: v for k, v in headers.items() if k.lower() not in sensitive_headers
            }
            transaction.set_data("http.headers", safe_headers)

            # Set user agent
            if "user-agent" in headers:
                transaction.set_tag("http.user_agent", headers["user-agent"])

            # Set client IP
            client_ip = request.client.host if request.client else "unknown"
            transaction.set_tag("http.client_ip", client_ip)

            try:
                # Process the request
                response = await call_next(request)

                # Set response context
                transaction.set_data("http.status_code", response.status_code)
                transaction.set_tag("http.status_code", response.status_code)

                # Mark as successful if status code is 2xx or 3xx
                if 200 <= response.status_code < 400:
                    transaction.set_status("ok")
                else:
                    transaction.set_status("internal_error")

                return response

            except Exception as exc:
                # Set error context
                transaction.set_status("internal_error")
                transaction.set_data("error", str(exc))

                # Capture the exception in Sentry
                sentry_sdk.capture_exception(exc)

                # Re-raise the exception
                raise
            finally:
                # Set duration
                duration = time.time() - start_time
                transaction.set_data("duration", duration)

                # Log slow requests
                if duration > 5.0:  # Log requests taking more than 5 seconds
                    logger.warning(
                        f"Slow request: {request.method} {request.url.path} "
                        f"took {duration:.2f}s"
                    )
