"""
cortex/api/middleware/request_tracer.py — Structured request tracing middleware.

Pure ASGI middleware (not BaseHTTPMiddleware) to avoid response-buffering
overhead.  Runs as the outermost middleware layer so it captures the total
request/response cycle time.

For every HTTP request it:
  1. Generates a unique 12-char hex ``X-Request-ID``
  2. Injects the ID into ``request.state`` so downstream handlers can reference it
  3. Appends the ID to the response headers
  4. Emits a structured JSON log line (method, path, status, latency_ms, owner_id)
  5. Writes an entry to the in-memory debug-log ring buffer
       (``cortex.api.debug_log``)

The middleware degrades gracefully — if any part fails the request is still
served; we never let tracing logic break real traffic.
"""

from __future__ import annotations

import json
import logging
import time
import uuid
from typing import Any

logger = logging.getLogger(__name__)


class RequestTracerMiddleware:
    """
    ASGI middleware that adds request-ID tracing and structured logging.

    Add as the *last* ``add_middleware`` call in ``create_app`` so it becomes
    the outermost layer and sees the request before any other middleware.

    Attributes:
        app: Inner ASGI application.
    """

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

    async def __call__(self, scope: Any, receive: Any, send: Any) -> None:
        if scope["type"] != "http":
            await self.app(scope, receive, send)
            return

        # ------------------------------------------------------------------
        # 1. Generate request ID and inject into scope state
        # ------------------------------------------------------------------
        request_id: str = uuid.uuid4().hex[:12]

        state = scope.get("state")
        if state is None:
            try:
                from starlette.datastructures import State as _State
                scope["state"] = _State()
            except ImportError:
                scope["state"] = _SimpleState()
        try:
            scope["state"].request_id = request_id
        except Exception:
            pass

        start_ts = time.perf_counter()

        # ------------------------------------------------------------------
        # 2. Wrap send() to capture status code + inject X-Request-ID header
        # ------------------------------------------------------------------
        status_code: list[int] = [500]

        async def _send_wrapper(message: Any) -> None:
            if message["type"] == "http.response.start":
                status_code[0] = message.get("status", 500)
                # Append X-Request-ID to response headers
                headers = list(message.get("headers", []))
                headers.append((b"x-request-id", request_id.encode()))
                message = {**message, "headers": headers}
            await send(message)

        # ------------------------------------------------------------------
        # 3. Dispatch to inner app
        # ------------------------------------------------------------------
        try:
            await self.app(scope, receive, _send_wrapper)
        finally:
            latency_ms = round((time.perf_counter() - start_ts) * 1000, 2)

            # Extract path / method from scope
            path = scope.get("path", "")
            method = scope.get("method", "")
            status = status_code[0]

            # Extract owner_id from request state (set by auth middleware)
            owner_id = ""
            try:
                state = scope.get("state")
                if state is not None:
                    owner_id = getattr(state, "owner_id", "") or ""
            except Exception:
                pass

            # ------------------------------------------------------------------
            # 4. Emit structured log line
            # ------------------------------------------------------------------
            try:
                logger.info(
                    json.dumps({
                        "event": "http_request",
                        "request_id": request_id,
                        "method": method,
                        "path": path,
                        "status": status,
                        "latency_ms": latency_ms,
                        "owner_id": owner_id,
                    })
                )
            except Exception:
                pass

            # ------------------------------------------------------------------
            # 5. Write to in-memory debug-log ring buffer
            # ------------------------------------------------------------------
            try:
                from cortex.api.debug_log import add_entry, DebugLogEntry
                add_entry(DebugLogEntry(
                    request_id=request_id,
                    method=method,
                    path=path,
                    status=status,
                    latency_ms=latency_ms,
                    owner_id=owner_id,
                    timestamp=time.time(),
                ))
            except Exception:
                pass


class _SimpleState:
    """Fallback state container if Starlette is unavailable."""

    def __setattr__(self, name: str, value: Any) -> None:
        object.__setattr__(self, name, value)

    def __getattr__(self, name: str) -> Any:
        raise AttributeError(name)
