# -*- coding: utf-8 -*-
"""
/knowledge 命令辅助函数测试
覆盖：分类分组列表、stats 统计、pin/archive 子命令
辅助函数为纯函数，接收 loader/curator，返回字符串列表，便于测试。
"""
from pathlib import Path

import pytest

from abyss.commands.knowledge import (
    format_knowledge_list_grouped,
    format_knowledge_stats,
    handle_knowledge_pin,
    handle_knowledge_archive,
)
from abyss.knowledge import Knowledge
from abyss.knowledge.curator import KnowledgeCurator


def _make_knowledge(name, description, level="project", category="experience",
                    tags=None) -> Knowledge:
    """构造测试用 Knowledge 对象。"""
    return Knowledge(
        name=name,
        description=description,
        content="正文",
        level=level,
        path=Path(f"/tmp/{name}.md"),
        tags=tags or [],
        category=category,
    )


class _FakeLoader:
    """假 KnowledgeLoader：list_all 返回预设列表。"""

    def __init__(self, items):
        self._items = items

    def list_all(self):
        return list(self._items)


# ---------- format_knowledge_list_grouped ----------

class TestFormatListGrouped:
    """分类分组列表显示。"""

    def test_groups_by_category(self):
        """同类型知识归到同一分组，按类型顺序输出。"""
        items = [
            _make_knowledge("pref-1", "偏好1", category="preference"),
            _make_knowledge("exp-1", "经验1", category="experience"),
            _make_knowledge("bug-1", "bug1", category="bugfix"),
            _make_knowledge("pref-2", "偏好2", category="preference"),
        ]
        lines = format_knowledge_list_grouped(_FakeLoader(items))
        text = "\n".join(lines)
        # 偏好 → bug → 经验 顺序
        assert text.index("偏好1") < text.index("bug1")
        assert text.index("bug1") < text.index("经验1")
        # 同组相邻
        pref1_idx = text.index("偏好1")
        pref2_idx = text.index("偏好2")
        bug_idx = text.index("bug1")
        assert pref1_idx < pref2_idx < bug_idx

    def test_includes_level_prefix(self):
        """每行含 [G]/[P] 级别前缀。"""
        items = [
            _make_knowledge("g-1", "全局", level="global", category="experience"),
            _make_knowledge("p-1", "项目", level="project", category="experience"),
        ]
        lines = format_knowledge_list_grouped(_FakeLoader(items))
        text = "\n".join(lines)
        assert "[G]" in text
        assert "[P]" in text

    def test_empty_loader_returns_empty(self):
        """空知识库返回空列表。"""
        lines = format_knowledge_list_grouped(_FakeLoader([]))
        assert lines == []

    def test_category_header_present(self):
        """分组包含中文类型标题。"""
        items = [_make_knowledge("p1", "偏好", category="preference")]
        lines = format_knowledge_list_grouped(_FakeLoader(items))
        text = "\n".join(lines)
        assert "偏好" in text  # 标题中含"偏好"


# ---------- format_knowledge_stats ----------

class TestFormatStats:
    """stats 统计显示。"""

    def test_counts_by_category(self, tmp_path):
        """统计按类型计数。"""
        items = [
            _make_knowledge("p1", "p", category="preference"),
            _make_knowledge("p2", "p", category="preference"),
            _make_knowledge("e1", "e", category="experience"),
            _make_knowledge("b1", "b", category="bugfix"),
        ]
        curator = KnowledgeCurator({"project": tmp_path, "global": tmp_path / "g"})
        lines = format_knowledge_stats(_FakeLoader(items), curator)
        text = "\n".join(lines)
        assert "preference" in text or "偏好" in text
        assert "2" in text  # preference 计数 2

    def test_includes_total(self, tmp_path):
        """包含总数。"""
        items = [
            _make_knowledge("p1", "p", category="preference"),
            _make_knowledge("e1", "e", category="experience"),
        ]
        curator = KnowledgeCurator({"project": tmp_path, "global": tmp_path / "g"})
        lines = format_knowledge_stats(_FakeLoader(items), curator)
        text = "\n".join(lines)
        assert "2" in text  # 总数 2


# ---------- handle_knowledge_pin ----------

class TestHandlePin:
    """pin 子命令。"""

    def test_pin_returns_success_message(self, tmp_path):
        """pin 成功返回成功消息。"""
        # 先在 project 级创建一个知识文件
        cat_dir = tmp_path / "experiences"
        cat_dir.mkdir(parents=True)
        md = cat_dir / "test-k.md"
        md.write_text(
            "---\nname: test-k\ndescription: d\nlevel: project\ncategory: experience\n---\n正文",
            encoding="utf-8",
        )
        curator = KnowledgeCurator({"project": tmp_path, "global": tmp_path / "g"})
        msg = handle_knowledge_pin(curator, "test-k")
        assert "成功" in msg or "OK" in msg or "pinned" in msg.lower()

    def test_pin_empty_name_returns_error(self, tmp_path):
        """空名称返回错误消息。"""
        curator = KnowledgeCurator({"project": tmp_path, "global": tmp_path / "g"})
        msg = handle_knowledge_pin(curator, "")
        assert "错误" in msg or "失败" in msg or "无效" in msg or "error" in msg.lower()


# ---------- handle_knowledge_archive ----------

class TestHandleArchive:
    """archive 子命令。"""

    def test_archive_moves_file(self, tmp_path):
        """archive 成功后原文件移到 .archive/。"""
        cat_dir = tmp_path / "experiences"
        cat_dir.mkdir(parents=True)
        md = cat_dir / "archived-k.md"
        md.write_text(
            "---\nname: archived-k\ndescription: d\nlevel: project\ncategory: experience\n---\n正文",
            encoding="utf-8",
        )
        curator = KnowledgeCurator({"project": tmp_path, "global": tmp_path / "g"})
        msg = handle_knowledge_archive(curator, "archived-k")
        assert "成功" in msg or "OK" in msg or "归档" in msg
        assert not md.exists()
        assert (tmp_path / ".archive" / "archived-k.md").exists()

    def test_archive_not_found_returns_error(self, tmp_path):
        """不存在的知识返回错误消息。"""
        curator = KnowledgeCurator({"project": tmp_path, "global": tmp_path / "g"})
        msg = handle_knowledge_archive(curator, "nonexistent-k")
        assert "错误" in msg or "失败" in msg or "未找到" in msg or "error" in msg.lower()
