# -*- coding: utf-8 -*-
"""
/knowledge 交互式菜单测试
覆盖：主菜单弹出、6 项动作分发、二级菜单选知识名、Esc 取消、空知识库处理。
"""
from pathlib import Path
from unittest.mock import patch, MagicMock

import click
import pytest

from abyss.commands.knowledge import (
    handle_knowledge_interactive,
    _KNOWLEDGE_MENU_OPTIONS,
)
from abyss.knowledge import Knowledge


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


class _FakeLoader:
    """假 KnowledgeLoader。"""

    def __init__(self, items=None):
        self._items = items or []

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

    def match_by_keyword(self, query, limit=20):
        return [k for k in self._items if query.lower() in k.description.lower()]

    def get(self, name):
        for k in self._items:
            if k.name == name:
                return k
        return None


# ---------- 基础行为 ----------

class TestInteractiveBasic:
    """交互式菜单基础行为。"""

    def test_returns_true_when_called(self):
        """handle_knowledge_interactive 返回 True（已处理）。"""
        loader = _FakeLoader([_make_knowledge("k1")])
        with patch("abyss.commands.knowledge.ansi_select", return_value=None):
            result = handle_knowledge_interactive(loader, curator=None)
        assert result is True

    def test_esc_returns_true_without_error(self):
        """用户按 Esc（ansi_select 返回 None）应返回 True 不报错。"""
        loader = _FakeLoader([_make_knowledge("k1")])
        with patch("abyss.commands.knowledge.ansi_select", return_value=None):
            result = handle_knowledge_interactive(loader, curator=None)
        assert result is True

    def test_menu_options_constant(self):
        """_KNOWLEDGE_MENU_OPTIONS 包含 6 项纯文本选项。"""
        assert len(_KNOWLEDGE_MENU_OPTIONS) == 6
        assert "列出全部知识" in _KNOWLEDGE_MENU_OPTIONS
        assert "查看统计" in _KNOWLEDGE_MENU_OPTIONS


# ---------- 动作分发 ----------

class TestInteractiveActions:
    """主菜单动作分发。"""

    def test_list_all_action(self, capsys):
        """选"列出全部知识"应输出分组列表。"""
        loader = _FakeLoader([
            _make_knowledge("k1", "描述1", category="experience"),
        ])
        with patch("abyss.commands.knowledge.ansi_select",
                   return_value="列出全部知识"):
            handle_knowledge_interactive(loader, curator=None)
        out = capsys.readouterr().out
        assert "k1" in out
        assert "描述1" in out

    def test_stats_action(self, capsys):
        """选"查看统计"应输出统计信息。"""
        loader = _FakeLoader([_make_knowledge("k1")])
        with patch("abyss.commands.knowledge.ansi_select",
                   return_value="查看统计"):
            handle_knowledge_interactive(loader, curator=None)
        out = capsys.readouterr().out
        assert "统计" in out

    def test_search_action_prompts_keyword(self, capsys):
        """选"搜索知识"应提示输入关键词并输出结果。"""
        loader = _FakeLoader([
            _make_knowledge("react-hooks", "react hooks 用法"),
        ])
        with patch("abyss.commands.knowledge.ansi_select",
                   return_value="搜索知识"), \
             patch("click.prompt", return_value="react"):
            handle_knowledge_interactive(loader, curator=None)
        out = capsys.readouterr().out
        assert "react-hooks" in out

    def test_show_action_secondary_menu(self, capsys):
        """选"查看详情"应弹二级菜单选知识名，输出完整内容。"""
        loader = _FakeLoader([_make_knowledge("k1", "描述1")])
        # 第一次 ansi_select 返回"查看详情"，第二次返回知识选项
        with patch("abyss.commands.knowledge.ansi_select",
                   side_effect=["查看详情", "[P] k1: 描述1"]):
            handle_knowledge_interactive(loader, curator=None)
        out = capsys.readouterr().out
        assert "k1 正文" in out

    def test_pin_action_secondary_menu(self, capsys):
        """选"固定知识"应弹二级菜单选知识名，调用 pin。"""
        loader = _FakeLoader([_make_knowledge("k1", "描述1")])
        fake_curator = MagicMock()
        fake_curator.pin_knowledge.return_value = True
        with patch("abyss.commands.knowledge.ansi_select",
                   side_effect=["固定知识", "[P] k1: 描述1"]):
            handle_knowledge_interactive(loader, curator=fake_curator)
        fake_curator.pin_knowledge.assert_called_once_with("k1")

    def test_archive_action_secondary_menu(self, capsys):
        """选"归档知识"应弹二级菜单选知识名，调用 archive。"""
        loader = _FakeLoader([_make_knowledge("k1", "描述1")])
        fake_curator = MagicMock()
        fake_curator.archive_knowledge.return_value = True
        with patch("abyss.commands.knowledge.ansi_select",
                   side_effect=["归档知识", "[P] k1: 描述1"]):
            handle_knowledge_interactive(loader, curator=fake_curator)
        fake_curator.archive_knowledge.assert_called_once_with("k1")


# ---------- 边界情况 ----------

class TestInteractiveEdgeCases:
    """交互式菜单边界情况。"""

    def test_empty_knowledge_pin_skips_secondary_menu(self, capsys):
        """空知识库选"固定知识"应提示无知识，不弹二级菜单。"""
        loader = _FakeLoader([])
        fake_curator = MagicMock()
        with patch("abyss.commands.knowledge.ansi_select",
                   return_value="固定知识"):
            handle_knowledge_interactive(loader, curator=fake_curator)
        out = capsys.readouterr().out
        assert "无知识" in out or "为空" in out
        fake_curator.pin_knowledge.assert_not_called()

    def test_empty_knowledge_archive_skips_secondary_menu(self, capsys):
        """空知识库选"归档知识"应提示无知识，不弹二级菜单。"""
        loader = _FakeLoader([])
        fake_curator = MagicMock()
        with patch("abyss.commands.knowledge.ansi_select",
                   return_value="归档知识"):
            handle_knowledge_interactive(loader, curator=fake_curator)
        out = capsys.readouterr().out
        assert "无知识" in out or "为空" in out
        fake_curator.archive_knowledge.assert_not_called()

    def test_show_esc_in_secondary_menu(self, capsys):
        """查看详情时二级菜单按 Esc 应不报错。"""
        loader = _FakeLoader([_make_knowledge("k1")])
        with patch("abyss.commands.knowledge.ansi_select",
                   side_effect=["查看详情", None]):
            result = handle_knowledge_interactive(loader, curator=None)
        assert result is True
