"""
config_lint.py — 配置静态检查工具
==================================

为 Critic 和 Optimizer agent 提供配置验证能力。
复用 lambdagent 的 schema 验证，并添加 builder 专用检查。
"""
from __future__ import annotations

import sys
from pathlib import Path
from typing import Dict, List, Tuple

import yaml

PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent.parent
sys.path.insert(0, str(PROJECT_ROOT))

# ── 常量 ──────────────────────────────────────────────────

VALID_TYPES = {"react", "chain", "router", "parallel"}

VALID_PROVIDERS = {
    "anthropic", "openai", "dashscope", "deepseek",
    "zhipu", "moonshot", "ollama",
}

PROVIDER_MODELS = {
    "anthropic": {"claude-sonnet-4-20250514", "claude-opus-4-20250514"},
    "openai": {"gpt-4o", "gpt-4o-mini"},
    "dashscope": {"qwen3-max-2026-01-23", "qwen-max", "qwen-plus"},
    "deepseek": {"deepseek-chat", "deepseek-reasoner"},
    "zhipu": {"glm-4-plus", "glm-4-flash"},
    "moonshot": {"moonshot-v1-128k"},
    "ollama": {"llama3", "qwen2"},
}

VALID_LOCAL_TOOLS = {
    "terminate", "shell", "file", "browser", "app",
    "system", "screenshot", "done",
}

REQUIRED_FIELDS = ["agentId", "name", "type", "systemPrompt", "model"]


# ── 检查函数 ──────────────────────────────────────────────

def lint_config(config: Dict | str) -> Dict:
    """
    对 agent 配置进行静态检查。

    Args:
        config: 配置字典或 YAML 字符串

    Returns:
        {
            "valid": bool,
            "errors": [{"level": "error", "rule": "R001", "message": "..."}],
            "warnings": [{"level": "warning", "rule": "W001", "message": "..."}],
            "score": int  # 0-100 质量分
        }
    """
    if isinstance(config, str):
        try:
            config = yaml.safe_load(config)
        except yaml.YAMLError as e:
            return {
                "valid": False,
                "errors": [{"level": "error", "rule": "R000", "message": f"YAML 解析失败: {e}"}],
                "warnings": [],
                "score": 0,
            }

    if not isinstance(config, dict):
        return {
            "valid": False,
            "errors": [{"level": "error", "rule": "R000", "message": "配置不是有效的字典"}],
            "warnings": [],
            "score": 0,
        }

    errors: List[Dict] = []
    warnings: List[Dict] = []

    # R001: 必填字段检查
    for field in REQUIRED_FIELDS:
        if field not in config:
            errors.append({
                "level": "error", "rule": "R001",
                "message": f"缺少必填字段: {field}",
            })

    # R002: type 合法性
    agent_type = config.get("type")
    if agent_type and agent_type not in VALID_TYPES:
        errors.append({
            "level": "error", "rule": "R002",
            "message": f"无效的 type: {agent_type}，合法值: {VALID_TYPES}",
        })

    # R003: model 配置
    model = config.get("model", {})
    if isinstance(model, dict):
        provider = model.get("provider")
        model_name = model.get("name")

        if provider and provider not in VALID_PROVIDERS:
            errors.append({
                "level": "error", "rule": "R003",
                "message": f"无效的 provider: {provider}，合法值: {VALID_PROVIDERS}",
            })

        if provider and model_name:
            valid_models = PROVIDER_MODELS.get(provider, set())
            if valid_models and model_name not in valid_models:
                warnings.append({
                    "level": "warning", "rule": "W003",
                    "message": f"model {model_name} 不在 {provider} 的已知模型列表中: {valid_models}",
                })

        temp = model.get("temperature")
        if temp is not None and (temp < 0.0 or temp > 2.0):
            errors.append({
                "level": "error", "rule": "R003",
                "message": f"temperature {temp} 超出范围 [0.0, 2.0]",
            })

    # R004: type 与子配置匹配
    if agent_type == "react" and "react" not in config:
        warnings.append({
            "level": "warning", "rule": "W004",
            "message": "type=react 但缺少 react 配置块（将使用默认值）",
        })
    if agent_type == "chain" and "chain" not in config:
        errors.append({
            "level": "error", "rule": "R004",
            "message": "type=chain 必须有 chain 配置块",
        })
    if agent_type == "router" and "router" not in config:
        errors.append({
            "level": "error", "rule": "R004",
            "message": "type=router 必须有 router 配置块",
        })

    # R005: systemPrompt 质量
    prompt = config.get("systemPrompt", "")
    if isinstance(prompt, str):
        if len(prompt.strip()) < 20:
            errors.append({
                "level": "error", "rule": "R005",
                "message": f"systemPrompt 过短（{len(prompt.strip())} 字符），至少 20 字符",
            })
        elif len(prompt.strip()) < 50:
            warnings.append({
                "level": "warning", "rule": "W005",
                "message": f"systemPrompt 较短（{len(prompt.strip())} 字符），建议 100+ 字符",
            })

    # R006: mcp.localTools 必须包含 terminate
    mcp = config.get("mcp", {})
    if isinstance(mcp, dict):
        local_tools = mcp.get("localTools", [])
        if isinstance(local_tools, list) and "terminate" not in local_tools:
            warnings.append({
                "level": "warning", "rule": "W006",
                "message": "mcp.localTools 缺少 terminate（agent 可能无法正常结束）",
            })

        # 检查工具名合法性
        for tool in local_tools:
            if tool not in VALID_LOCAL_TOOLS and not tool.startswith("call_"):
                warnings.append({
                    "level": "warning", "rule": "W006",
                    "message": f"未知的 localTool: {tool}",
                })

    # R007: onlineTool 必须有对应的 app.mcp.custom.nodes
    online_tools = mcp.get("onlineTool", {}) if isinstance(mcp, dict) else {}
    if online_tools:
        app_nodes = _nested_get(config, "app", "mcp", "custom", "nodes") or {}
        for server_name in online_tools:
            if server_name not in app_nodes:
                errors.append({
                    "level": "error", "rule": "R007",
                    "message": f"onlineTool 引用 {server_name} 但 app.mcp.custom.nodes 中缺少其配置",
                })

    # W008: react.maxSteps 合理性
    react_config = config.get("react", {})
    if isinstance(react_config, dict):
        max_steps = react_config.get("maxSteps", 10)
        if max_steps > 30:
            warnings.append({
                "level": "warning", "rule": "W008",
                "message": f"react.maxSteps={max_steps} 过大，可能导致 token 浪费",
            })
        if max_steps < 2:
            warnings.append({
                "level": "warning", "rule": "W008",
                "message": f"react.maxSteps={max_steps} 过小，agent 可能无法完成任务",
            })

    # W009: guard 缺失
    if "guard" not in config:
        warnings.append({
            "level": "warning", "rule": "W009",
            "message": "缺少 guard 配置（建议至少设置 maxOutputLength）",
        })

    # ── 计算总分 ──
    score = 100
    score -= len(errors) * 15
    score -= len(warnings) * 5
    score = max(0, min(100, score))

    return {
        "valid": len(errors) == 0,
        "errors": errors,
        "warnings": warnings,
        "score": score,
    }


def _nested_get(d: dict, *keys):
    for k in keys:
        if isinstance(d, dict):
            d = d.get(k)
        else:
            return None
    return d
