"""ExpertTemplateRegistry 单元测试"""

from __future__ import annotations

import os
import tempfile

import pytest
import yaml

from agentkit.experts.config import ExpertConfig, ExpertTemplate
from agentkit.experts.registry import ExpertTemplateRegistry


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


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


def _write_yaml_file(directory: str, filename: str, data: dict) -> str:
    """写入临时 YAML 文件并返回路径"""
    filepath = os.path.join(directory, filename)
    with open(filepath, "w", encoding="utf-8") as f:
        yaml.dump(data, f, allow_unicode=True)
    return filepath


# ── ExpertTemplateRegistry 测试 ───────────────────────────


class TestExpertTemplateRegistry:
    """ExpertTemplateRegistry 注册中心测试"""

    def test_register_and_get(self):
        """注册并获取模板"""
        registry = ExpertTemplateRegistry()
        template = _make_template("analyst", persona="分析师")
        registry.register(template)

        result = registry.get("analyst")
        assert result is not None
        assert result.name == "analyst"
        assert result.config.persona == "分析师"

    def test_get_nonexistent_returns_none(self):
        """获取不存在的模板返回 None"""
        registry = ExpertTemplateRegistry()
        assert registry.get("nonexistent") is None

    def test_list_all_templates(self):
        """列出所有模板"""
        registry = ExpertTemplateRegistry()
        registry.register(_make_template("a", description="模板A"))
        registry.register(_make_template("b", description="模板B"))
        registry.register(_make_template("c", description="模板C"))

        templates = registry.list()
        names = {t.name for t in templates}
        assert names == {"a", "b", "c"}

    def test_list_empty_registry(self):
        """空注册中心返回空列表"""
        registry = ExpertTemplateRegistry()
        assert registry.list() == []

    def test_search_by_name_case_insensitive(self):
        """按名称搜索（大小写不敏感）"""
        registry = ExpertTemplateRegistry()
        registry.register(_make_template("DataAnalyst", description="数据分析师"))
        registry.register(_make_template("CodeReviewer", description="代码审查员"))

        results = registry.search("data")
        assert len(results) == 1
        assert results[0].name == "DataAnalyst"

        results = registry.search("CODEREVIEWER")
        assert len(results) == 1
        assert results[0].name == "CodeReviewer"

    def test_search_by_description(self):
        """按描述搜索"""
        registry = ExpertTemplateRegistry()
        registry.register(_make_template("analyst", description="数据分析专家"))
        registry.register(_make_template("writer", description="内容创作专家"))

        results = registry.search("数据")
        assert len(results) == 1
        assert results[0].name == "analyst"

        results = registry.search("创作")
        assert len(results) == 1
        assert results[0].name == "writer"

    def test_search_no_matches_returns_empty(self):
        """搜索无匹配返回空列表"""
        registry = ExpertTemplateRegistry()
        registry.register(_make_template("analyst", description="数据分析师"))

        results = registry.search("nonexistent_keyword")
        assert results == []

    def test_register_overwrites_same_name(self):
        """同名模板注册覆盖旧模板"""
        registry = ExpertTemplateRegistry()
        v1 = _make_template("expert_a", persona="版本1", description="旧版本")
        v2 = _make_template("expert_a", persona="版本2", description="新版本")

        registry.register(v1)
        registry.register(v2)

        result = registry.get("expert_a")
        assert result is not None
        assert result.config.persona == "版本2"
        assert result.description == "新版本"
        # 确保只有一个
        assert len(registry.list()) == 1

    def test_load_from_yaml(self):
        """从 YAML 文件加载模板"""
        yaml_data = {
            "name": "yaml_expert",
            "is_builtin": False,
            "description": "YAML 加载的专家",
            "config": {
                "name": "yaml_expert",
                "agent_type": "expert",
                "persona": "YAML 专家",
                "thinking_style": "结构化思维",
                "collaboration_strategy": "cooperative",
                "bound_skills": ["skill_a", "skill_b"],
                "avatar": "A",
                "color": "#fa8c16",
                "is_lead": False,
                "task_mode": "llm_generate",
                "prompt": {"identity": "YAML 专家"},
            },
        }

        with tempfile.TemporaryDirectory() as tmpdir:
            filepath = _write_yaml_file(tmpdir, "expert.yaml", yaml_data)
            registry = ExpertTemplateRegistry()
            template = registry.load_from_yaml(filepath)

            assert template.name == "yaml_expert"
            assert template.config.persona == "YAML 专家"
            assert template.config.thinking_style == "结构化思维"
            assert template.config.bound_skills == ["skill_a", "skill_b"]
            assert template.config.avatar == "A"
            assert template.config.color == "#fa8c16"
            # 同时注册到 registry
            assert registry.get("yaml_expert") is template

    def test_load_from_directory(self):
        """从目录批量加载模板"""
        yaml_data_a = {
            "name": "dir_expert_a",
            "description": "目录专家A",
            "config": {
                "name": "dir_expert_a",
                "agent_type": "expert",
                "persona": "专家A",
                "task_mode": "llm_generate",
                "prompt": {"identity": "专家A"},
            },
        }
        yaml_data_b = {
            "name": "dir_expert_b",
            "description": "目录专家B",
            "config": {
                "name": "dir_expert_b",
                "agent_type": "expert",
                "persona": "专家B",
                "task_mode": "llm_generate",
                "prompt": {"identity": "专家B"},
            },
        }

        with tempfile.TemporaryDirectory() as tmpdir:
            _write_yaml_file(tmpdir, "expert_a.yaml", yaml_data_a)
            _write_yaml_file(tmpdir, "expert_b.yml", yaml_data_b)
            # 非 YAML 文件应被忽略
            with open(os.path.join(tmpdir, "readme.txt"), "w") as f:
                f.write("not a yaml")

            registry = ExpertTemplateRegistry()
            loaded = registry.load_from_directory(tmpdir)

            assert len(loaded) == 2
            names = {t.name for t in loaded}
            assert "dir_expert_a" in names
            assert "dir_expert_b" in names
            # 同时注册到 registry
            assert registry.get("dir_expert_a") is not None
            assert registry.get("dir_expert_b") is not None

    def test_load_from_directory_nonexistent(self):
        """从不存在的目录加载返回空列表"""
        registry = ExpertTemplateRegistry()
        loaded = registry.load_from_directory("/nonexistent/path")
        assert loaded == []

    def test_load_from_yaml_invalid_format(self):
        """加载非字典格式的 YAML 抛出异常"""
        with tempfile.TemporaryDirectory() as tmpdir:
            filepath = os.path.join(tmpdir, "invalid.yaml")
            with open(filepath, "w", encoding="utf-8") as f:
                yaml.dump(["not", "a", "dict"], f)

            registry = ExpertTemplateRegistry()
            with pytest.raises(Exception):
                registry.load_from_yaml(filepath)
