"""Agent 上报接收路由（数据写入 metrics.db）"""

import threading
import time

from fastapi import APIRouter, Depends, HTTPException, status
from sqlmodel import Session, select

from app.core.config import settings
from app.core.security import require_agent_key
from app.core.timeutils import utcnow
from app.database import assets_engine, get_metrics_session
from app.models.asset import Asset
from app.models.monitor import EventLog, MetricPoint, SecurityCheck, ServerInfo
from app.schemas.agent import AgentReport

router = APIRouter(prefix="/api/agent", tags=["agent"])

# 内存级频率限制：{asset_id: 上次上报的 monotonic 时间戳}
# 同步路由运行在线程池，多线程并发读写需加锁保证“读-判断-写”原子性
_last_report_ts: dict = {}
_rate_limit_lock = threading.Lock()


def _check_rate_limit(asset_id: int) -> None:
    """限制同一资产的上报频率，防止配置错误的 Agent 高频写入填满数据库"""
    interval = settings.AGENT_REPORT_MIN_INTERVAL
    if interval <= 0:
        return
    now = time.monotonic()
    with _rate_limit_lock:
        last = _last_report_ts.get(asset_id)
        if last is not None and (now - last) < interval:
            raise HTTPException(
                status_code=status.HTTP_429_TOO_MANY_REQUESTS,
                detail=f"上报过于频繁，同一资产最小间隔 {interval} 秒",
            )
        _last_report_ts[asset_id] = now


def _validate_asset_id(asset_id: int) -> None:
    """校验上报的 asset_id 在资产库中真实存在，防止脏数据写入"""
    with Session(assets_engine) as session:
        asset = session.get(Asset, asset_id)
    if not asset:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"asset_id={asset_id} 不存在，请先在资产列表中登记该服务器",
        )


@router.post(
    "/report",
    summary="Agent 上报资源/信息/安全状态",
    dependencies=[Depends(require_agent_key)],
)
def report(data: AgentReport, session: Session = Depends(get_metrics_session)) -> dict:
    _validate_asset_id(data.asset_id)
    _check_rate_limit(data.asset_id)

    # 资源监控时序
    if data.metrics:
        session.add(MetricPoint(asset_id=data.asset_id, **data.metrics.model_dump()))

    # 服务器信息快照（每资产一条，覆盖更新）
    if data.server_info:
        info = data.server_info.model_dump()
        info["last_seen"] = utcnow()
        existing = session.exec(
            select(ServerInfo).where(ServerInfo.asset_id == data.asset_id)
        ).first()
        if existing:
            for key, value in info.items():
                setattr(existing, key, value)
            session.add(existing)
        else:
            session.add(ServerInfo(asset_id=data.asset_id, **info))

    # 安全检查项
    if data.security:
        for check in data.security:
            session.add(SecurityCheck(asset_id=data.asset_id, **check.model_dump()))

    # 事件日志
    session.add(
        EventLog(
            level="info",
            source="agent",
            asset_id=data.asset_id,
            message=f"Agent 上报 asset_id={data.asset_id}",
        )
    )
    session.commit()
    return {"status": "ok", "asset_id": data.asset_id}
