"""监控中间件"""
import time
from typing import Callable

from fastapi import Request, Response
from starlette.middleware.base import BaseHTTPMiddleware

from app.monitoring.metrics import (
    API_REQUESTS_TOTAL,
    API_REQUEST_DURATION_SECONDS,
    API_REQUESTS_IN_PROGRESS,
)

# 需要排除的路径（不记录指标）
EXCLUDED_PATHS = {"/health", "/ready", "/metrics", "/docs", "/openapi.json"}


class MonitoringMiddleware(BaseHTTPMiddleware):
    """API监控中间件"""

    async def dispatch(self, request: Request, call_next: Callable) -> Response:
        # 跳过排除路径
        if request.url.path in EXCLUDED_PATHS:
            return await call_next(request)

        # 提取端点标识（用于指标标签）
        endpoint = self._get_endpoint_label(request)

        # 增加活跃请求计数
        API_REQUESTS_IN_PROGRESS.labels(
            method=request.method,
            endpoint=endpoint
        ).inc()

        # 记录开始时间
        start_time = time.perf_counter()

        try:
            # 执行请求
            response = await call_next(request)
            status_code = response.status_code
        except Exception as e:
            status_code = 500
            raise
        finally:
            # 计算耗时
            duration = time.perf_counter() - start_time

            # 记录指标
            API_REQUESTS_TOTAL.labels(
                method=request.method,
                endpoint=endpoint,
                status=str(status_code)
            ).inc()

            API_REQUEST_DURATION_SECONDS.labels(
                method=request.method,
                endpoint=endpoint
            ).observe(duration)

            # 减少活跃请求计数
            API_REQUESTS_IN_PROGRESS.labels(
                method=request.method,
                endpoint=endpoint
            ).dec()

        return response

    def _get_endpoint_label(self, request: Request) -> str:
        """提取端点标签"""
        path = request.url.path

        # 规范化路径（替换ID等参数）
        parts = path.strip("/").split("/")

        # 处理常见模式：/api/v1/resources/{id}
        if len(parts) >= 4 and parts[0] == "api":
            resource = parts[2] if len(parts) > 2 else "unknown"
            action = parts[3] if len(parts) > 3 else "list"

            # 映射到规范标签
            if action.isdigit():
                return f"{resource}_detail"
            return f"{resource}_{action}"

        return "other"
