"""F16 Skill System 单元测试

按 test-plan-v2.6.md §F16：
- T1: 注册/查询（P0）
- T2: 三层自由度（P0）
- T3: 技能匹配（P0）
- T4: 模板变量替换（P1）
"""

import json
import pytest
from pathlib import Path

from src.daemon.skill_system import (
    Skill,
    SkillExecutor,
    SkillFreedom,
    SkillRegistry,
)


# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------

@pytest.fixture
def registry():
    return SkillRegistry()


@pytest.fixture
def registry_with_skills(registry):
    registry.register(Skill(
        id="code-review",
        name="Code Review",
        description="Review code quality and suggest improvements",
        freedom=SkillFreedom.HIGH.value,
        tags=["coding", "review"],
    ))
    registry.register(Skill(
        id="test-template",
        name="Test Generator",
        description="Generate test cases from code",
        freedom=SkillFreedom.MEDIUM.value,
        prompt_template="Write tests for {{module}} focusing on {{focus}}",
        tags=["testing", "coding"],
    ))
    registry.register(Skill(
        id="deploy-script",
        name="Deploy Script",
        description="Run deployment script",
        freedom=SkillFreedom.LOW.value,
        script_path="/usr/local/bin/deploy.sh",
        tags=["deployment"],
    ))
    return registry


@pytest.fixture
def executor(registry_with_skills):
    return SkillExecutor(registry=registry_with_skills)


@pytest.fixture
def skills_dir(tmp_path):
    d = tmp_path / "skills"
    d.mkdir()
    (d / "skill1.json").write_text(json.dumps({
        "id": "loaded-skill",
        "name": "Loaded Skill",
        "description": "Loaded from file",
        "freedom": "high",
    }))
    (d / "skill2.json").write_text(json.dumps({
        "id": "another",
        "name": "Another",
        "description": "Another skill",
        "freedom": "medium",
        "prompt_template": "Do {{action}}",
    }))
    return d


# ---------------------------------------------------------------------------
# T1: 注册/查询
# ---------------------------------------------------------------------------

class TestRegistry:
    def test_register_and_get(self, registry):
        skill = Skill(id="s1", name="Test", description="A test skill")
        registry.register(skill)
        assert registry.get("s1") is not None
        assert registry.get("s1").name == "Test"

    def test_unregister(self, registry):
        registry.register(Skill(id="s1", name="T", description="D"))
        assert registry.unregister("s1") is True
        assert registry.get("s1") is None

    def test_unregister_nonexistent(self, registry):
        assert registry.unregister("nope") is False

    def test_list_skills(self, registry_with_skills):
        skills = registry_with_skills.list_skills()
        assert len(skills) == 3

    def test_list_by_freedom(self, registry_with_skills):
        high = registry_with_skills.list_skills(freedom=SkillFreedom.HIGH.value)
        assert len(high) == 1
        assert high[0].id == "code-review"

    def test_list_by_tags(self, registry_with_skills):
        coding = registry_with_skills.list_skills(tags=["coding"])
        assert len(coding) == 2

    def test_list_enabled_only(self, registry):
        registry.register(Skill(id="s1", name="On", description="D", enabled=True))
        registry.register(Skill(id="s2", name="Off", description="D", enabled=False))
        assert len(registry.list_skills()) == 1

    def test_load_from_dir(self, skills_dir):
        reg = SkillRegistry(skills_dir=skills_dir)
        assert reg.count() == 2
        assert reg.get("loaded-skill") is not None


# ---------------------------------------------------------------------------
# T2: 三层自由度
# ---------------------------------------------------------------------------

class TestFreedomLevels:
    def test_high_freedom_prompt(self, executor):
        result = executor.build_prompt("code-review")
        assert "Skill: Code Review" in result
        assert "Principle" in result

    def test_medium_freedom_template(self, executor):
        result = executor.build_prompt("test-template",
                                        variables={"module": "auth.py", "focus": "edge cases"})
        assert "auth.py" in result
        assert "edge cases" in result

    def test_low_freedom_script(self, executor):
        result = executor.build_prompt("deploy-script")
        assert "deploy.sh" in result


# ---------------------------------------------------------------------------
# T3: 技能匹配
# ---------------------------------------------------------------------------

class TestMatching:
    def test_match_by_name(self, registry_with_skills):
        results = registry_with_skills.match("review")
        assert len(results) >= 1
        assert results[0][0].id == "code-review"
        assert results[0][1] > 0  # score > 0

    def test_match_by_description(self, registry_with_skills):
        results = registry_with_skills.match("deployment")
        assert len(results) >= 1

    def test_match_with_task_type(self, registry_with_skills):
        results = registry_with_skills.match("test", task_type="testing")
        assert len(results) >= 1

    def test_match_no_results(self, registry_with_skills):
        results = registry_with_skills.match("nonexistent_xyz_abc")
        assert len(results) == 0

    def test_match_limit(self, registry_with_skills):
        results = registry_with_skills.match("code", limit=1)
        assert len(results) <= 1

    def test_match_excludes_disabled(self, registry):
        registry.register(Skill(id="s1", name="Disabled Review",
                                description="review", enabled=False))
        results = registry.match("review")
        assert all(s.enabled for s, _ in results)


# ---------------------------------------------------------------------------
# T4: 模板变量替换 + 执行
# ---------------------------------------------------------------------------

class TestExecution:
    def test_execute_high_freedom(self, executor):
        result = executor.execute("code-review")
        assert result["status"] == "success"
        assert result["prompt"] is not None

    def test_execute_medium_with_vars(self, executor):
        result = executor.execute("test-template",
                                   context={"module": "api", "focus": "errors"})
        assert result["status"] == "success"
        assert "api" in result["prompt"]

    def test_execute_low_blocked(self, executor):
        """默认不允许脚本执行"""
        result = executor.execute("deploy-script")
        assert result["status"] == "error"
        assert "not allowed" in result["error"]

    def test_execute_low_allowed(self, registry_with_skills):
        executor = SkillExecutor(registry=registry_with_skills, allow_scripts=True)
        result = executor.execute("deploy-script")
        assert result["status"] == "success"

    def test_execute_not_found(self, executor):
        result = executor.execute("nonexistent")
        assert result["status"] == "error"

    def test_execute_disabled(self, registry):
        registry.register(Skill(id="s1", name="T", description="D", enabled=False))
        executor = SkillExecutor(registry=registry)
        result = executor.execute("s1")
        assert result["status"] == "error"
        assert "disabled" in result["error"]

    def test_execution_log(self, executor):
        executor.execute("code-review")
        assert len(executor.execution_log) == 1
        assert executor.execution_log[0]["skill_id"] == "code-review"
