"""
测试 ServerSkillLoader
=====================

测试 SKILL.md 加载器的功能和效果。

测试内容：
1. parse_skill_metadata() - 解析 SKILL.md 的 YAML frontmatter
2. list_skills() - 列出技能目录中的所有技能
3. 边界情况测试（无效文件、缺失字段、空目录等）

使用示例：
    pytest langchain_agentx/middleware/test_server_skill_loader.py -v
    python langchain_agentx/middleware/test_server_skill_loader.py
    python langchain_agentx/middleware/test_server_skill_loader.py 1  # 仅测试 parse_skill_metadata
    python langchain_agentx/middleware/test_server_skill_loader.py 2  # 仅测试 list_skills
"""
import sys
import tempfile
from pathlib import Path

# 确保项目根目录在 Python 路径中（支持直接运行 python test_xxx.py）
_project_root = Path(__file__).resolve().parent.parent.parent
if str(_project_root) not in sys.path:
    sys.path.insert(0, str(_project_root))

from langchain_agentx.middleware.server_skill_loader import (
    parse_skill_metadata,
    list_skills,
    SkillMetadata,
)


def print_section(title: str, width: int = 80):
    """打印分隔符"""
    print("\n" + "=" * width)
    print(title)
    print("=" * width)


def test_parse_skill_metadata_basic():
    """测试基本 YAML frontmatter 解析"""
    print_section("测试 parse_skill_metadata - 基本格式")

    with tempfile.TemporaryDirectory() as tmpdir:
        skill_dir = Path(tmpdir) / "test_skill"
        skill_dir.mkdir()
        skill_md_path = skill_dir / "SKILL.md"

        skill_md_content = """---
name: test_skill
description: 这是一个测试技能
---

# Test Skill

这是技能的内容。
"""
        skill_md_path.write_text(skill_md_content, encoding="utf-8")

        metadata = parse_skill_metadata(skill_md_path, source="user")

        assert metadata is not None, "应该成功解析"
        assert metadata["name"] == "test_skill", "name 字段应该正确"
        assert metadata["description"] == "这是一个测试技能", "description 字段应该正确"
        assert metadata["source"] == "user", "source 字段应该正确"
        assert metadata["path"] == str(skill_md_path), "path 字段应该正确"

        print(f"✓ 基本格式解析成功")
        print(f"  name: {metadata['name']}")
        print(f"  description: {metadata['description']}")
        print(f"  source: {metadata['source']}")
        print(f"  path: {metadata['path']}")


def test_parse_skill_metadata_with_tool_groups():
    """测试包含 tool_groups 的 YAML frontmatter"""
    print_section("测试 parse_skill_metadata - 包含 tool_groups")

    with tempfile.TemporaryDirectory() as tmpdir:
        skill_dir = Path(tmpdir) / "test_skill"
        skill_dir.mkdir()
        skill_md_path = skill_dir / "SKILL.md"

        skill_md_content = """---
name: architecture_analysis
description: 分析代码库架构并生成完整的4+1架构视图

tool_groups:
  - community_exploration
  - code_structure
  - dependency_analysis
---

# Architecture Analysis Skill

这是技能的内容。
"""
        skill_md_path.write_text(skill_md_content, encoding="utf-8")

        metadata = parse_skill_metadata(skill_md_path, source="user")

        assert metadata is not None, "应该成功解析"
        assert metadata["name"] == "architecture_analysis", "name 字段应该正确"
        assert "tool_groups" in metadata, "应该包含 tool_groups 字段"
        assert isinstance(metadata["tool_groups"], list), "tool_groups 应该是列表"
        assert len(metadata["tool_groups"]) == 3, "tool_groups 应该有 3 个元素"
        assert "community_exploration" in metadata["tool_groups"], "应该包含 community_exploration"
        assert "code_structure" in metadata["tool_groups"], "应该包含 code_structure"
        assert "dependency_analysis" in metadata["tool_groups"], "应该包含 dependency_analysis"

        print(f"✓ 包含 tool_groups 的格式解析成功")
        print(f"  name: {metadata['name']}")
        print(f"  tool_groups: {metadata['tool_groups']}")


def test_parse_skill_metadata_with_comments():
    """测试包含注释的 YAML frontmatter"""
    print_section("测试 parse_skill_metadata - 包含注释")

    with tempfile.TemporaryDirectory() as tmpdir:
        skill_dir = Path(tmpdir) / "test_skill"
        skill_dir.mkdir()
        skill_md_path = skill_dir / "SKILL.md"

        skill_md_content = """---
name: test_skill
description: 这是一个测试技能
# 这是注释，应该被忽略
tool_groups:
  - group1
  - group2
---

# Test Skill
"""
        skill_md_path.write_text(skill_md_content, encoding="utf-8")

        metadata = parse_skill_metadata(skill_md_path, source="user")

        assert metadata is not None, "应该成功解析（忽略注释）"
        assert metadata["name"] == "test_skill", "name 字段应该正确"
        assert "tool_groups" in metadata, "应该包含 tool_groups 字段"

        print(f"✓ 包含注释的格式解析成功（注释被忽略）")


def test_parse_skill_metadata_missing_required_fields():
    """测试缺失必需字段的情况"""
    print_section("测试 parse_skill_metadata - 缺失必需字段")

    with tempfile.TemporaryDirectory() as tmpdir:
        skill_dir = Path(tmpdir) / "test_skill"
        skill_dir.mkdir()
        skill_md_path = skill_dir / "SKILL.md"

        skill_md_content = """---
description: 这是一个测试技能
---

# Test Skill
"""
        skill_md_path.write_text(skill_md_content, encoding="utf-8")

        metadata = parse_skill_metadata(skill_md_path, source="user")
        assert metadata is None, "缺失必需字段应该返回 None"

        print(f"✓ 缺失 name 字段时正确返回 None")

        skill_md_content = """---
name: test_skill
---

# Test Skill
"""
        skill_md_path.write_text(skill_md_content, encoding="utf-8")

        metadata = parse_skill_metadata(skill_md_path, source="user")
        assert metadata is None, "缺失 description 字段应该返回 None"

        print(f"✓ 缺失 description 字段时正确返回 None")


def test_parse_skill_metadata_no_frontmatter():
    """测试没有 YAML frontmatter 的情况"""
    print_section("测试 parse_skill_metadata - 没有 frontmatter")

    with tempfile.TemporaryDirectory() as tmpdir:
        skill_dir = Path(tmpdir) / "test_skill"
        skill_dir.mkdir()
        skill_md_path = skill_dir / "SKILL.md"

        skill_md_content = """# Test Skill

这是技能的内容，但没有 frontmatter。
"""
        skill_md_path.write_text(skill_md_content, encoding="utf-8")

        metadata = parse_skill_metadata(skill_md_path, source="user")
        assert metadata is None, "没有 frontmatter 应该返回 None"

        print(f"✓ 没有 frontmatter 时正确返回 None")


def test_parse_skill_metadata_invalid_file():
    """测试无效文件（不存在、编码错误等）"""
    print_section("测试 parse_skill_metadata - 无效文件")

    non_existent_path = Path("/non/existent/path/SKILL.md")
    metadata = parse_skill_metadata(non_existent_path, source="user")
    assert metadata is None, "不存在的文件应该返回 None"

    print(f"✓ 不存在的文件正确返回 None")


def test_list_skills_basic():
    """测试基本技能列表功能"""
    print_section("测试 list_skills - 基本功能")

    with tempfile.TemporaryDirectory() as tmpdir:
        skills_dir = Path(tmpdir)

        skill1_dir = skills_dir / "skill1"
        skill1_dir.mkdir()
        skill1_md = skill1_dir / "SKILL.md"
        skill1_md.write_text("""---
name: skill1
description: 第一个技能
---
""", encoding="utf-8")

        skill2_dir = skills_dir / "skill2"
        skill2_dir.mkdir()
        skill2_md = skill2_dir / "SKILL.md"
        skill2_md.write_text("""---
name: skill2
description: 第二个技能
tool_groups:
  - group1
  - group2
---
""", encoding="utf-8")

        skills = list_skills(skills_dir)

        assert len(skills) == 2, "应该找到 2 个技能"
        skill_names = [s["name"] for s in skills]
        assert "skill1" in skill_names, "应该包含 skill1"
        assert "skill2" in skill_names, "应该包含 skill2"

        for skill in skills:
            assert skill["source"] == "user", "所有技能的 source 应该是 'user'"

        print(f"✓ 成功列出 {len(skills)} 个技能")
        for skill in skills:
            print(f"  - {skill['name']}: {skill['description']}")


def test_list_skills_empty_directory():
    """测试空目录"""
    print_section("测试 list_skills - 空目录")

    with tempfile.TemporaryDirectory() as tmpdir:
        skills_dir = Path(tmpdir)

        skills = list_skills(skills_dir)
        assert len(skills) == 0, "空目录应该返回空列表"

        print(f"✓ 空目录正确返回空列表")


def test_list_skills_missing_skill_md():
    """测试缺少 SKILL.md 的目录"""
    print_section("测试 list_skills - 缺少 SKILL.md")

    with tempfile.TemporaryDirectory() as tmpdir:
        skills_dir = Path(tmpdir)

        skill_dir = skills_dir / "skill_without_md"
        skill_dir.mkdir()

        skills = list_skills(skills_dir)
        assert len(skills) == 0, "缺少 SKILL.md 的目录应该被忽略"

        print(f"✓ 缺少 SKILL.md 的目录被正确忽略")


def test_list_skills_invalid_skill_md():
    """测试无效的 SKILL.md（缺失必需字段）"""
    print_section("测试 list_skills - 无效的 SKILL.md")

    with tempfile.TemporaryDirectory() as tmpdir:
        skills_dir = Path(tmpdir)

        skill_dir = skills_dir / "invalid_skill"
        skill_dir.mkdir()
        skill_md = skill_dir / "SKILL.md"
        skill_md.write_text("""---
description: 无效的技能（缺失 name）
---
""", encoding="utf-8")

        skills = list_skills(skills_dir)
        assert len(skills) == 0, "无效的 SKILL.md 应该被忽略"

        print(f"✓ 无效的 SKILL.md 被正确忽略")


def test_list_skills_mixed_valid_invalid():
    """测试混合有效和无效技能的情况"""
    print_section("测试 list_skills - 混合有效和无效技能")

    with tempfile.TemporaryDirectory() as tmpdir:
        skills_dir = Path(tmpdir)

        valid_skill_dir = skills_dir / "valid_skill"
        valid_skill_dir.mkdir()
        valid_skill_md = valid_skill_dir / "SKILL.md"
        valid_skill_md.write_text("""---
name: valid_skill
description: 有效的技能
---
""", encoding="utf-8")

        invalid_skill_dir = skills_dir / "invalid_skill"
        invalid_skill_dir.mkdir()
        invalid_skill_md = invalid_skill_dir / "SKILL.md"
        invalid_skill_md.write_text("""---
description: 无效的技能
---
""", encoding="utf-8")

        no_md_dir = skills_dir / "no_md_skill"
        no_md_dir.mkdir()

        skills = list_skills(skills_dir)

        assert len(skills) == 1, "应该只找到 1 个有效技能"
        assert skills[0]["name"] == "valid_skill", "应该找到 valid_skill"

        print(f"✓ 混合情况下正确过滤，找到 {len(skills)} 个有效技能")


def test_list_skills_real_directory():
    """测试真实的技能目录（如果存在）"""
    print_section("测试 list_skills - 真实目录")

    # 从项目根目录查找 skill/server_skill
    current_file = Path(__file__).resolve()
    project_root = current_file.parent.parent.parent
    real_skills_dir = project_root / "skill" / "server_skill"

    if not real_skills_dir.exists():
        print(f"⚠ 真实技能目录不存在: {real_skills_dir}")
        print("  跳过真实目录测试")
        return

    skills = list_skills(real_skills_dir)

    print(f"✓ 从真实目录找到 {len(skills)} 个技能")
    for skill in skills:
        print(f"  - {skill['name']}: {skill['description']}")
        if "tool_groups" in skill:
            print(f"    tool_groups: {skill['tool_groups']}")


def run_all_tests():
    """运行所有测试"""
    print_section("ServerSkillLoader 测试套件")

    test_functions = [
        test_parse_skill_metadata_basic,
        test_parse_skill_metadata_with_tool_groups,
        test_parse_skill_metadata_with_comments,
        test_parse_skill_metadata_missing_required_fields,
        test_parse_skill_metadata_no_frontmatter,
        test_parse_skill_metadata_invalid_file,
        test_list_skills_basic,
        test_list_skills_empty_directory,
        test_list_skills_missing_skill_md,
        test_list_skills_invalid_skill_md,
        test_list_skills_mixed_valid_invalid,
        test_list_skills_real_directory,
    ]

    passed = 0
    failed = 0

    for test_func in test_functions:
        try:
            test_func()
            passed += 1
        except AssertionError as e:
            print(f"❌ {test_func.__name__} 失败: {e}")
            failed += 1
        except Exception as e:
            print(f"❌ {test_func.__name__} 异常: {e}")
            import traceback
            traceback.print_exc()
            failed += 1

    print_section("测试结果")
    print(f"通过: {passed}")
    print(f"失败: {failed}")
    print(f"总计: {passed + failed}")

    if failed == 0:
        print("\n✅ 所有测试通过！")
    else:
        print(f"\n❌ {failed} 个测试失败")


if __name__ == "__main__":
    import sys

    if len(sys.argv) > 1:
        test_num = sys.argv[1]
        if test_num == "1":
            print_section("仅测试 parse_skill_metadata")
            test_parse_skill_metadata_basic()
            test_parse_skill_metadata_with_tool_groups()
            test_parse_skill_metadata_with_comments()
            test_parse_skill_metadata_missing_required_fields()
            test_parse_skill_metadata_no_frontmatter()
            test_parse_skill_metadata_invalid_file()
        elif test_num == "2":
            print_section("仅测试 list_skills")
            test_list_skills_basic()
            test_list_skills_empty_directory()
            test_list_skills_missing_skill_md()
            test_list_skills_invalid_skill_md()
            test_list_skills_mixed_valid_invalid()
            test_list_skills_real_directory()
        else:
            print(f"未知的测试编号: {test_num}")
            print("使用: python test_server_skill_loader.py [1|2]")
    else:
        run_all_tests()
