"""ExpertTeamRouter 单元测试"""

from __future__ import annotations

from agentkit.experts.config import ExpertConfig, ExpertTemplate
from agentkit.experts.registry import ExpertTemplateRegistry
from agentkit.experts.router import (
    ExpertTeamRouter,
    ExpertTeamRoutingResult,
    TEAM_PREFIX_PATTERN,
)


# ── 辅助函数 ──────────────────────────────────────────────


def _make_template(
    name: str = "test_template",
    persona: str = "测试专家",
    bound_skills: list[str] | None = None,
) -> ExpertTemplate:
    """创建测试用 ExpertTemplate 实例"""
    config = ExpertConfig(
        name=name,
        agent_type="expert",
        persona=persona,
        thinking_style="analytical",
        bound_skills=bound_skills or [],
        task_mode="llm_generate",
        prompt={"identity": persona},
    )
    return ExpertTemplate(
        name=name,
        config=config,
        is_builtin=True,
        description=f"{name} 模板",
    )


def _make_registry_with_templates() -> ExpertTemplateRegistry:
    """创建包含预注册模板的注册中心"""
    registry = ExpertTemplateRegistry()
    registry.register(_make_template("analyst", persona="数据分析师", bound_skills=["data_query"]))
    registry.register(_make_template("strategist", persona="策略专家", bound_skills=["planning"]))
    registry.register(_make_template("reviewer", persona="代码审查员", bound_skills=["code_review"]))
    return registry


# ── TEAM_PREFIX_PATTERN 正则测试 ──────────────────────────


class TestTeamPrefixPattern:
    """TEAM_PREFIX_PATTERN 正则匹配测试"""

    def test_match_team_only(self):
        """@team 前缀匹配"""
        match = TEAM_PREFIX_PATTERN.match("@team 分析这个数据")
        assert match is not None
        assert match.group(1) is None
        assert match.group(2) == "分析这个数据"

    def test_match_team_with_experts(self):
        """@team:expert1,expert2 前缀匹配"""
        match = TEAM_PREFIX_PATTERN.match("@team:analyst,strategist 分析这个数据")
        assert match is not None
        assert match.group(1) == "analyst,strategist"
        assert match.group(2) == "分析这个数据"

    def test_match_team_single_expert(self):
        """@team:expert 前缀匹配"""
        match = TEAM_PREFIX_PATTERN.match("@team:analyst 分析数据")
        assert match is not None
        assert match.group(1) == "analyst"
        assert match.group(2) == "分析数据"

    def test_match_team_no_task(self):
        """@team 无后续任务内容"""
        match = TEAM_PREFIX_PATTERN.match("@team")
        assert match is not None
        assert match.group(1) is None
        assert match.group(2) == ""

    def test_no_match_without_prefix(self):
        """无 @team 前缀不匹配"""
        match = TEAM_PREFIX_PATTERN.match("分析这个数据")
        assert match is None

    def test_no_match_team_in_middle(self):
        """@team 在中间不匹配（必须开头）"""
        match = TEAM_PREFIX_PATTERN.match("请 @team 分析数据")
        assert match is None

    def test_match_team_with_leading_whitespace(self):
        """@team 前有空白字符时正则不匹配（由 resolve() 中 strip() 处理）"""
        match = TEAM_PREFIX_PATTERN.match("  @team:analyst 任务内容")
        # 正则使用 ^ 锚定，前导空白不匹配
        assert match is None


# ── ExpertTeamRoutingResult 默认值测试 ────────────────────


class TestExpertTeamRoutingResult:
    """ExpertTeamRoutingResult 数据类测试"""

    def test_default_values(self):
        """默认值验证"""
        result = ExpertTeamRoutingResult()
        assert result.matched is False
        assert result.team_mode is False
        assert result.specified_experts == []
        assert result.task_content == ""
        assert result.auto_compose is False
        assert result.match_method == ""


# ── ExpertTeamRouter.resolve 测试 ──────────────────────────


class TestExpertTeamRouterResolve:
    """ExpertTeamRouter.resolve 方法测试"""

    def test_team_prefix_triggers_team_mode(self):
        """@team 前缀触发团队模式"""
        router = ExpertTeamRouter()
        result = router.resolve("@team 分析这个数据")
        assert result.matched is True
        assert result.team_mode is True
        assert result.match_method == "explicit_team"
        assert result.task_content == "分析这个数据"

    def test_team_with_experts_specifies_members(self):
        """@team:analyst,strategist 指定专家成员"""
        router = ExpertTeamRouter()
        result = router.resolve("@team:analyst,strategist 分析数据")
        assert result.matched is True
        assert result.team_mode is True
        assert result.specified_experts == ["analyst", "strategist"]
        assert result.auto_compose is False
        assert result.match_method == "explicit_team"

    def test_team_no_experts_uses_default_template(self):
        """@team 无指定专家时使用默认 dev_team 模板"""
        router = ExpertTeamRouter()
        result = router.resolve("@team 分析数据")
        assert result.matched is True
        assert result.team_mode is True
        # 默认 dev_team 模板包含 5 个成员
        assert result.specified_experts == [
            "tech_lead",
            "frontend_engineer",
            "backend_engineer",
            "qa_engineer",
            "code_reviewer",
        ]
        assert result.auto_compose is False

    def test_team_with_expert_extracts_task(self):
        """@team:analyst 正确提取任务内容"""
        router = ExpertTeamRouter()
        result = router.resolve("@team:analyst 请分析这份报告")
        assert result.matched is True
        assert result.specified_experts == ["analyst"]
        assert result.task_content == "请分析这份报告"

    def test_no_team_prefix_no_team_mode(self):
        """无 @team 前缀不触发团队模式"""
        router = ExpertTeamRouter()
        result = router.resolve("普通问题")
        assert result.matched is False
        assert result.team_mode is False
        assert result.task_content == "普通问题"
        assert result.match_method == ""

    def test_nonexistent_expert_still_included(self):
        """指定不存在的专家名仍包含在列表中"""
        router = ExpertTeamRouter()
        result = router.resolve("@team:analyst,nonexistent 任务")
        assert result.specified_experts == ["analyst", "nonexistent"]

    def test_team_with_empty_task_uses_full_content(self):
        """@team 无任务内容时 task_content 使用原始内容"""
        router = ExpertTeamRouter()
        result = router.resolve("@team")
        assert result.task_content == "@team"
        # 无指定专家时使用默认模板
        assert result.auto_compose is False
        assert len(result.specified_experts) == 5

    def test_resolve_strips_leading_whitespace(self):
        """resolve() 对前导空白做 strip()"""
        router = ExpertTeamRouter()
        result = router.resolve("  @team:analyst 任务内容")
        assert result.matched is True
        assert result.team_mode is True
        assert result.specified_experts == ["analyst"]
        assert result.task_content == "任务内容"

    def test_invalid_expert_names_rejected(self):
        """无效专家名被过滤掉"""
        router = ExpertTeamRouter()
        # 包含特殊字符的名称应被过滤
        result = router.resolve("@team:analyst,inva!id,bad/name 任务")
        # 仅保留合法名称
        assert "analyst" in result.specified_experts
        assert "inva!id" not in result.specified_experts
        assert "bad/name" not in result.specified_experts

    def test_max_experts_limit(self):
        """指定超过 MAX_EXPERTS 数量的专家时截断"""
        router = ExpertTeamRouter()
        # 构造 15 个专家名
        names = ",".join(f"expert{i}" for i in range(15))
        result = router.resolve(f"@team:{names} 任务")
        from agentkit.experts.router import MAX_EXPERTS

        assert len(result.specified_experts) == MAX_EXPERTS


# ── ExpertTeamRouter.resolve_expert_configs 测试 ───────────


class TestExpertTeamRouterResolveExpertConfigs:
    """ExpertTeamRouter.resolve_expert_configs 方法测试"""

    def test_resolve_existing_templates(self):
        """解析已注册模板返回对应 ExpertConfig"""
        registry = _make_registry_with_templates()
        router = ExpertTeamRouter(template_registry=registry)

        configs = router.resolve_expert_configs(["analyst", "strategist"])
        assert len(configs) == 2
        assert configs[0].name == "analyst"
        assert configs[0].persona == "数据分析师"
        assert configs[1].name == "strategist"
        assert configs[1].persona == "策略专家"

    def test_resolve_nonexistent_creates_dynamic_config(self):
        """解析不存在的名称创建动态 ExpertConfig"""
        registry = _make_registry_with_templates()
        router = ExpertTeamRouter(template_registry=registry)

        configs = router.resolve_expert_configs(["analyst", "unknown_expert"])
        assert len(configs) == 2
        assert configs[0].name == "analyst"
        assert configs[0].persona == "数据分析师"
        # 动态生成的配置
        assert configs[1].name == "unknown_expert"
        assert configs[1].persona == "Expert in unknown_expert"
        assert configs[1].agent_type == "expert"
        assert configs[1].thinking_style == "analytical"
        assert configs[1].bound_skills == []
        assert configs[1].is_lead is False
        assert configs[1].task_mode == "llm_generate"

    def test_resolve_all_nonexistent(self):
        """所有名称都不存在时全部动态生成"""
        router = ExpertTeamRouter()
        configs = router.resolve_expert_configs(["role_a", "role_b"])
        assert len(configs) == 2
        assert configs[0].name == "role_a"
        assert configs[0].persona == "Expert in role_a"
        assert configs[1].name == "role_b"
        assert configs[1].persona == "Expert in role_b"

    def test_resolve_empty_list(self):
        """空列表返回空结果"""
        router = ExpertTeamRouter()
        configs = router.resolve_expert_configs([])
        assert configs == []

    def test_resolve_preserves_template_skills(self):
        """解析已注册模板保留 bound_skills"""
        registry = _make_registry_with_templates()
        router = ExpertTeamRouter(template_registry=registry)

        configs = router.resolve_expert_configs(["analyst"])
        assert configs[0].bound_skills == ["data_query"]

    def test_resolve_first_expert_is_lead(self):
        """第一个专家被指定为 lead"""
        registry = _make_registry_with_templates()
        router = ExpertTeamRouter(template_registry=registry)

        configs = router.resolve_expert_configs(["analyst", "strategist", "reviewer"])
        assert configs[0].is_lead is True
        assert configs[1].is_lead is False
        assert configs[2].is_lead is False

    def test_resolve_skips_invalid_names(self):
        """跳过无效的专家名"""
        router = ExpertTeamRouter()
        # 包含无效字符的名称应被跳过
        configs = router.resolve_expert_configs(["valid_name", "inva!id", "also_valid"])
        assert len(configs) == 2
        assert configs[0].name == "valid_name"
        assert configs[1].name == "also_valid"


# ── ExpertTeamRouter 构造测试 ─────────────────────────────


class TestExpertTeamRouterInit:
    """ExpertTeamRouter 构造函数测试"""

    def test_default_registry(self):
        """无参构造创建默认注册中心"""
        router = ExpertTeamRouter()
        assert router._registry is not None

    def test_custom_registry(self):
        """传入自定义注册中心"""
        registry = ExpertTemplateRegistry()
        router = ExpertTeamRouter(template_registry=registry)
        assert router._registry is registry


# ── ExpertTeamRouter.can_handle 测试 ──────────────────────


class TestExpertTeamRouterCanHandle:
    """ExpertTeamRouter.can_handle 方法测试"""

    def test_can_handle_with_matching_template_name(self):
        """模板名出现在内容中时返回 True"""
        registry = _make_registry_with_templates()
        router = ExpertTeamRouter(template_registry=registry)

        assert router.can_handle("请 analyst 帮我分析") is True

    def test_can_handle_with_matching_description(self):
        """模板描述词出现在内容中时返回 True"""
        registry = _make_registry_with_templates()
        router = ExpertTeamRouter(template_registry=registry)

        # 描述为 "analyst 模板" 等，"模板" 长度 > 2 应匹配
        assert router.can_handle("我需要一个模板来参考") is True

    def test_can_handle_no_templates(self):
        """无注册模板时返回 False"""
        router = ExpertTeamRouter()
        # 默认注册中心可能为空
        # 强制清空以测试
        router._registry._templates.clear()
        assert router.can_handle("任何内容") is False

    def test_can_handle_with_templates_no_match(self):
        """有模板但内容不匹配时仍返回 True（auto-compose 可组建团队）"""
        registry = _make_registry_with_templates()
        router = ExpertTeamRouter(template_registry=registry)

        # 内容与任何模板名/描述都不匹配，但有模板存在 → auto-compose 可用
        assert router.can_handle("完全无关的内容 xyz123") is True


# ── ExpertTeamRouter 团队模板展开测试 (U7) ─────────────────


class TestExpertTeamRouterTemplateExpansion:
    """ExpertTeamRouter 团队模板展开测试 — @team:dev_team 自动展开为成员列表"""

    def test_team_template_expands_to_members(self):
        """@team:dev_team 展开为 dev_team 模板的 bound_skills 成员列表"""
        registry = ExpertTemplateRegistry()
        # 注册 dev_team 模板（bound_skills 存储成员列表）
        registry.register(
            _make_template(
                "dev_team",
                persona="编程团队",
                bound_skills=["tech_lead", "frontend_engineer", "backend_engineer"],
            )
        )
        router = ExpertTeamRouter(template_registry=registry)

        result = router.resolve("@team:dev_team 实现用户登录功能")
        assert result.matched is True
        assert result.team_mode is True
        assert result.specified_experts == ["tech_lead", "frontend_engineer", "backend_engineer"]
        assert result.auto_compose is False
        assert result.match_method == "explicit_team"

    def test_team_template_respects_max_experts_limit(self):
        """团队模板展开时遵守 MAX_EXPERTS 上限"""
        registry = ExpertTemplateRegistry()
        # 注册一个 bound_skills 超过 MAX_EXPERTS 的模板
        registry.register(
            _make_template(
                "dev_team",
                persona="编程团队",
                bound_skills=[f"expert{i}" for i in range(15)],
            )
        )
        router = ExpertTeamRouter(template_registry=registry)

        result = router.resolve("@team:dev_team 任务")
        from agentkit.experts.router import MAX_EXPERTS

        assert len(result.specified_experts) == MAX_EXPERTS

    def test_team_template_not_found_falls_back_to_default_members(self):
        """@team:unknown_template 时未知模板名作为普通专家处理"""
        registry = ExpertTemplateRegistry()
        router = ExpertTeamRouter(template_registry=registry)

        result = router.resolve("@team:unknown_template 任务")
        # 未知模板名（无 bound_skills）→ 作为普通专家名处理
        assert result.specified_experts == ["unknown_template"]

    def test_no_experts_uses_default_dev_team_template(self):
        """@team 无指定专家时从 dev_team 模板加载成员"""
        registry = ExpertTemplateRegistry()
        registry.register(
            _make_template(
                "dev_team",
                persona="编程团队",
                bound_skills=["tech_lead", "frontend_engineer", "backend_engineer"],
            )
        )
        router = ExpertTeamRouter(template_registry=registry)

        result = router.resolve("@team 实现功能")
        assert result.specified_experts == ["tech_lead", "frontend_engineer", "backend_engineer"]
        assert result.auto_compose is False

    def test_no_experts_no_dev_team_template_uses_hardcoded_fallback(self):
        """无 dev_team 模板时使用硬编码 fallback 列表"""
        registry = ExpertTemplateRegistry()
        router = ExpertTeamRouter(template_registry=registry)

        result = router.resolve("@team 任务")
        assert result.specified_experts == [
            "tech_lead",
            "frontend_engineer",
            "backend_engineer",
            "qa_engineer",
            "code_reviewer",
        ]

    def test_team_template_with_empty_bound_skills_treated_as_expert_name(self):
        """bound_skills 为空的模板名作为普通专家处理"""
        registry = ExpertTemplateRegistry()
        registry.register(_make_template("empty_team", persona="空团队", bound_skills=[]))
        router = ExpertTeamRouter(template_registry=registry)

        result = router.resolve("@team:empty_team 任务")
        # bound_skills 为空 → 不展开，作为普通专家名
        assert result.specified_experts == ["empty_team"]

    def test_default_template_class_attribute(self):
        """DEFAULT_TEMPLATE 类属性为 'dev_team'"""
        from agentkit.experts.router import ExpertTeamRouter as Router

        assert Router.DEFAULT_TEMPLATE == "dev_team"

    def test_load_default_template_members_with_template(self):
        """_load_default_template_members 从 dev_team 模板加载"""
        registry = ExpertTemplateRegistry()
        registry.register(
            _make_template(
                "dev_team",
                persona="编程团队",
                bound_skills=["a", "b", "c"],
            )
        )
        router = ExpertTeamRouter(template_registry=registry)

        members = router._load_default_template_members()
        assert members == ["a", "b", "c"]

    def test_load_default_template_members_without_template(self):
        """_load_default_template_members 无模板时返回硬编码列表"""
        registry = ExpertTemplateRegistry()
        router = ExpertTeamRouter(template_registry=registry)

        members = router._load_default_template_members()
        assert members == [
            "tech_lead",
            "frontend_engineer",
            "backend_engineer",
            "qa_engineer",
            "code_reviewer",
        ]
