"""Small ASGI middleware for request telemetry, privacy and cookie-origin checks."""

from time import perf_counter
from uuid import uuid4

import structlog
from starlette.datastructures import Headers, MutableHeaders
from starlette.responses import JSONResponse
from starlette.types import ASGIApp, Message, Receive, Scope, Send

from app.config import settings

logger = structlog.get_logger(__name__)


class HTTPPolicyMiddleware:
    """No request-body buffering or background tasks; supports streaming responses."""

    def __init__(self, app: ASGIApp) -> None:
        self.app = app

    async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
        if scope["type"] != "http":
            await self.app(scope, receive, send)
            return
        started = perf_counter()
        request_id = str(uuid4())
        headers = Headers(scope=scope)
        # Cookie authentication needs an origin check for unsafe requests. Bearer
        # authentication is not ambient browser authority; it doesn't need CSRF.
        cookie = headers.get("cookie", "")
        uses_cookie = any(name in cookie for name in ("access_token=", "refresh_token=", "patient_token="))
        origin = headers.get("origin")
        unsafe = scope["method"] not in {"GET", "HEAD", "OPTIONS"}
        if unsafe and uses_cookie and not headers.get("authorization"):
            invalid_origin = origin is not None and origin not in settings.APP_ALLOWED_ORIGINS
            if invalid_origin or (settings.is_production and origin is None):
                response = JSONResponse(
                    {"success": False, "error": {"code": "FORBIDDEN", "message": "Request origin denied."}},
                    status_code=403,
                )
                await response(scope, receive, send)
                return

        async def send_with_policy(message: Message) -> None:
            if message["type"] == "http.response.start":
                response_headers = MutableHeaders(scope=message)
                response_headers["X-Request-ID"] = request_id
                response_headers["X-Content-Type-Options"] = "nosniff"
                response_headers["Referrer-Policy"] = "no-referrer"
                response_headers["X-Frame-Options"] = "DENY"
                response_headers["Server-Timing"] = f"app;dur={(perf_counter() - started) * 1000:.1f}"
                if scope["path"].startswith("/api/"):
                    response_headers["Cache-Control"] = "no-store"
                # Route template only: never log patient phones, bearer links,
                # query strings or raw paths containing patient identifiers.
                route = scope.get("route")
                logger.info(
                    "http_response",
                    request_id=request_id,
                    method=scope["method"],
                    route=getattr(route, "path", "unmatched"),
                    status=message["status"],
                    duration_ms=round((perf_counter() - started) * 1000, 1),
                )
            await send(message)

        await self.app(scope, receive, send_with_policy)
