# -*- coding: utf-8 -*-
"""
全局审计中间件
拦截所有入站 HTTP 请求，记录：方法、URL、客户端 IP、耗时、响应状态码。
日志输出到标准 logging，生产环境可对接 ELK / Loki 等日志收集系统。
"""

import logging
import time

from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
from starlette.requests import Request
from starlette.responses import Response

# 配置审计专用 logger，与业务日志分离
audit_logger = logging.getLogger("audit")
audit_logger.setLevel(logging.INFO)


class AuditMiddleware(BaseHTTPMiddleware):
    """
    审计中间件 - 记录每个请求的关键信息。
    日志格式: [AUDIT] <客户端IP> <方法> <URL> <状态码> <耗时ms>
    """

    async def dispatch(
        self, request: Request, call_next: RequestResponseEndpoint
    ) -> Response:
        # 提取客户端真实 IP (优先取反向代理传递的 X-Forwarded-For)
        client_ip = request.headers.get(
            "X-Forwarded-For", request.client.host if request.client else "unknown"
        )
        method = request.method
        url = str(request.url)

        start_time = time.perf_counter()

        try:
            response = await call_next(request)
        except Exception:
            # 未捕获异常也要记录审计日志
            elapsed_ms = (time.perf_counter() - start_time) * 1000
            audit_logger.error(
                "[AUDIT] %s %s %s 500 %.1fms (unhandled exception)",
                client_ip, method, url, elapsed_ms,
            )
            raise

        elapsed_ms = (time.perf_counter() - start_time) * 1000

        audit_logger.info(
            "[AUDIT] %s %s %s %d %.1fms",
            client_ip, method, url, response.status_code, elapsed_ms,
        )

        # 将审计信息注入响应头 (方便调试，生产环境可移除)
        response.headers["X-Request-Duration-Ms"] = f"{elapsed_ms:.1f}"

        return response
