"""安全评分服务

基于 Agent 上报的安全检查项（SecurityCheck）计算安全评分与加固建议。
评分按检查项加权：pass 满分、warn 半分、fail/unknown 零分。
"""

from sqlalchemy import func
from sqlmodel import Session, select

from app.models.monitor import SecurityCheck

# 检查项权重（总和约 100）
CHECK_WEIGHTS = {"ssh_config": 40, "firewall": 35, "listening_ports": 25}
DEFAULT_WEIGHT = 20


def latest_checks(session: Session, asset_id: int) -> list:
    """取每个检查项的最新一条

    用 GROUP BY max(ts) 子查询精确取每项最新记录：若用 limit(50) 后去重，
    当某个检查项连续上报超过 50 次时会把其他项的最新记录挤出，导致评分失真。
    """
    latest_ts = (
        select(
            SecurityCheck.check_item,
            func.max(SecurityCheck.ts).label("max_ts"),
        )
        .where(SecurityCheck.asset_id == asset_id)
        .group_by(SecurityCheck.check_item)
        .subquery()
    )
    stmt = select(SecurityCheck).where(SecurityCheck.asset_id == asset_id).join(
        latest_ts,
        (SecurityCheck.check_item == latest_ts.c.check_item)
        & (SecurityCheck.ts == latest_ts.c.max_ts),
    )
    return list(session.exec(stmt).all())


def compute_security_score(checks: list):
    """计算安全评分（0-100），无检查数据返回 None"""
    if not checks:
        return None
    total = 0
    earned = 0.0
    for check in checks:
        weight = CHECK_WEIGHTS.get(check.check_item, DEFAULT_WEIGHT)
        total += weight
        if check.status == "pass":
            earned += weight
        elif check.status == "warn":
            earned += weight * 0.5
    return round(earned / total * 100) if total else None


def score_level(score):
    """评分分级：good(>=80) / warning(>=50) / risk(<50) / unknown"""
    if score is None:
        return "unknown"
    if score >= 80:
        return "good"
    if score >= 50:
        return "warning"
    return "risk"


def get_asset_security(session: Session, asset_id: int) -> dict:
    """单个服务器的安全评分与加固建议"""
    checks = latest_checks(session, asset_id)
    score = compute_security_score(checks)
    return {
        "asset_id": asset_id,
        "score": score,
        "level": score_level(score),
        "checks_count": len(checks),
        "suggestions": [c.suggestion for c in checks if c.suggestion],
    }


def get_security_overview(session: Session) -> list:
    """所有有安全检查数据的服务器评分总览（按评分升序，风险高的在前）

    性能优化：用子查询取每个 (asset_id, check_item) 的最新 ts，仅拉取最新记录，
    避免全表扫描（历史数据量大时内存可控）。
    """
    latest_ts = (
        select(
            SecurityCheck.asset_id,
            SecurityCheck.check_item,
            func.max(SecurityCheck.ts).label("max_ts"),
        )
        .group_by(SecurityCheck.asset_id, SecurityCheck.check_item)
        .subquery()
    )
    stmt = select(SecurityCheck).join(
        latest_ts,
        (SecurityCheck.asset_id == latest_ts.c.asset_id)
        & (SecurityCheck.check_item == latest_ts.c.check_item)
        & (SecurityCheck.ts == latest_ts.c.max_ts),
    )
    all_checks = session.exec(stmt).all()

    # 按 asset_id 分组
    latest_by_asset: dict = {}
    for check in all_checks:
        latest_by_asset.setdefault(check.asset_id, []).append(check)

    result = []
    for asset_id, checks in latest_by_asset.items():
        score = compute_security_score(checks)
        result.append({
            "asset_id": asset_id,
            "score": score,
            "level": score_level(score),
            "checks_count": len(checks),
            "suggestions": [c.suggestion for c in checks if c.suggestion],
        })
    result.sort(key=lambda x: (x["score"] is None, x["score"] if x["score"] is not None else 0))
    return result
