"""
cortex/api/middleware/debug_logger.py — Debug request/response logging middleware.

Intercepts every HTTP request and logs:
  - timestamp, method, path
  - request body summary (first 500 chars)
  - response status, response body summary (first 500 chars)
  - latency_ms
  - session_id (extracted from request body or query params)

When debug logging is disabled: a zero-cost no-op passthrough.
When enabled: adds <1ms overhead (no I/O, no DB writes, just deque append).

Dependencies: cortex.debug (stdlib only).
"""

from __future__ import annotations

import time
from typing import Any

try:
    from starlette.middleware.base import BaseHTTPMiddleware
    from starlette.requests import Request
    from starlette.responses import Response
    _STARLETTE_AVAILABLE = True
except ImportError:
    _STARLETTE_AVAILABLE = False


if _STARLETTE_AVAILABLE:

    class DebugLoggerMiddleware(BaseHTTPMiddleware):
        """FastAPI/Starlette middleware for debug request/response logging."""

        async def dispatch(self, request: Request, call_next: Any) -> Response:
            from cortex.debug import is_debug_enabled, get_debug_buffer, DebugLogEntry

            # Fast path: if debug logging is disabled, skip everything
            if not is_debug_enabled():
                return await call_next(request)

            start = time.perf_counter()

            # Capture request body summary (best-effort, won't block on large bodies)
            request_body_summary = ""
            try:
                body_bytes = await request.body()
                if body_bytes:
                    request_body_summary = body_bytes[:500].decode("utf-8", errors="replace")
            except Exception:
                request_body_summary = "<read error>"

            # Extract session_id from query params or body
            session_id = request.query_params.get("session_id", "")
            if not session_id and request_body_summary:
                import json
                try:
                    parsed = json.loads(body_bytes)
                    if isinstance(parsed, dict):
                        session_id = str(parsed.get("session_id", ""))
                except (json.JSONDecodeError, Exception):
                    pass

            # Call the actual handler
            response = await call_next(request)

            latency_ms = (time.perf_counter() - start) * 1000

            # Capture response body summary (need to read streaming response)
            response_body_summary = ""
            try:
                body_chunks = []
                async for chunk in response.body_iterator:
                    body_chunks.append(chunk if isinstance(chunk, bytes) else chunk.encode())
                response_body = b"".join(body_chunks)
                response_body_summary = response_body[:500].decode("utf-8", errors="replace")

                from starlette.responses import Response as StarletteResponse
                response = StarletteResponse(
                    content=response_body,
                    status_code=response.status_code,
                    headers=dict(response.headers),
                    media_type=response.media_type,
                )
            except Exception:
                response_body_summary = "<read error>"

            # Log to ring buffer
            buf = get_debug_buffer()
            buf.append(DebugLogEntry(
                timestamp=time.time(),
                event_type="http_request",
                data={
                    "method": request.method,
                    "path": str(request.url.path),
                    "request_body_summary": request_body_summary,
                    "status": response.status_code,
                    "response_body_summary": response_body_summary,
                    "latency_ms": round(latency_ms, 2),
                    "session_id": session_id,
                },
            ))

            return response

else:
    class DebugLoggerMiddleware:  # type: ignore[no-redef]
        def __init__(self, app: Any, **kwargs: Any) -> None:
            self.app = app

        async def __call__(self, scope: Any, receive: Any, send: Any) -> None:
            await self.app(scope, receive, send)
