"""性能监控 + 告警 + Sentry 集成 + API 调用日志写入"""

import asyncio
import time
import uuid

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

from app.core.logging_config import get_logger

logger = get_logger("scilit.performance")

SENTRY_DSN: str | None = None

# 告警统计（一分钟窗口）
_alert_window: dict[str, tuple[int, float]] = {}
ALERT_THRESHOLD_5XX_PER_MIN = 10  # 每分钟超过 10 次 5xx 就告警


def init_sentry(dsn: str | None = None):
    global SENTRY_DSN
    SENTRY_DSN = dsn
    if dsn:
        try:
            import sentry_sdk
            sentry_sdk.init(dsn=dsn, traces_sample_rate=0.1, profiles_sample_rate=0.05)
            logger.info("[SENTRY] initialized")
        except ImportError:
            logger.warning("[SENTRY] sentry-sdk not installed")


class PerformanceMiddleware(BaseHTTPMiddleware):
    SLOW_THRESHOLD_MS = 1000

    async def dispatch(self, request: Request, call_next):
        start = time.perf_counter()
        response = await call_next(request)
        elapsed = (time.perf_counter() - start) * 1000

        if elapsed > self.SLOW_THRESHOLD_MS:
            logger.warning(f"[SLOW] {request.method} {request.url.path} — {elapsed:.0f}ms")

        response.headers["X-Response-Time-Ms"] = f"{elapsed:.0f}"

        # 5xx 告警
        if response.status_code >= 500:
            _check_alert(request.url.path)

        # Sentry
        if SENTRY_DSN and response.status_code >= 500:
            try:
                import sentry_sdk
                sentry_sdk.capture_exception()
            except Exception:
                pass

        return response


_EXCLUDED_API_PATHS = ("/health", "/api/v1/auth/", "/api/v1/analytics/")
_api_log_buffer: list[dict] = []
_api_log_lock = asyncio.Lock()
_api_log_last_flush = time.time()


async def _flush_api_logs():
    """将缓冲区中的 API 日志批量写入数据库"""
    global _api_log_last_flush
    async with _api_log_lock:
        if not _api_log_buffer:
            return
        batch = _api_log_buffer[:]
        _api_log_buffer.clear()
        _api_log_last_flush = time.time()

    try:
        from app.db import async_session
        from app.models.operations import ApiUsageLog

        async with async_session() as db:
            for item in batch:
                db.add(ApiUsageLog(
                    tenant_id=uuid.UUID(item["tenant_id"]) if isinstance(item["tenant_id"], str) else item["tenant_id"],
                    endpoint=item["endpoint"],
                    method=item["method"],
                    status_code=item["status_code"],
                    response_ms=item["response_ms"],
                    ip_address=item["ip_address"],
                ))
            await db.commit()
    except Exception:
        logger.warning("[API_LOG] batch flush failed", exc_info=True)


class RequestLoggingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        start = time.perf_counter()
        response = await call_next(request)
        elapsed = (time.perf_counter() - start) * 1000
        logger.info(f"{request.method} {request.url.path} → {response.status_code} ({elapsed:.0f}ms)")

        # 只记录已认证的 API 请求（排除健康检查、认证、分析等高频低价值端点）
        path = request.url.path
        if any(path.startswith(p) for p in _EXCLUDED_API_PATHS):
            return response

        from app.core.tenant_context import tenant_ctx
        tid = tenant_ctx.get()
        if not tid:
            return response

        _api_log_buffer.append({
            "tenant_id": tid,
            "endpoint": path,
            "method": request.method,
            "status_code": response.status_code,
            "response_ms": int(elapsed),
            "ip_address": request.client.host if request.client else None,
        })

        # 每 5 秒或满 100 条触发批量写入
        if len(_api_log_buffer) >= 100 or (time.time() - _api_log_last_flush) >= 5:
            asyncio.create_task(_flush_api_logs())

        return response


def _check_alert(path: str):
    now = time.time()
    bucket_key = f"5xx:{now // 60}"
    count, _ = _alert_window.get(bucket_key, (0, 0))
    count += 1
    _alert_window[bucket_key] = (count, now)
    # 清理旧窗口
    for k in list(_alert_window.keys()):
        if now - _alert_window[k][1] > 120:
            del _alert_window[k]
    if count >= ALERT_THRESHOLD_5XX_PER_MIN and count == ALERT_THRESHOLD_5XX_PER_MIN:
        logger.error(f"[ALERT] {count} 5xx errors in 1 minute! Last: {path}")


def get_metrics() -> dict:
    """返回当前监控指标"""
    return {
        "recent_5xx": {k: v[0] for k, v in _alert_window.items()},
        "alert_active": any(v[0] >= ALERT_THRESHOLD_5XX_PER_MIN for v in _alert_window.values()),
    }
