"""业务命名 Hook 集成测试。"""

from types import SimpleNamespace
from typing import Any

import os
import sys

import pytest

# 确保项目根目录在 sys.path 中
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
# SDK 包路径
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "packages", "maibot-plugin-sdk"))


class _FakeHookManager:
    """用于业务 Hook 测试的最小运行时管理器。"""

    def __init__(self, responses: dict[str, SimpleNamespace]) -> None:
        """初始化测试管理器。

        Args:
            responses: 按 Hook 名称预设的返回结果映射。
        """

        self._responses = responses
        self.calls: list[tuple[str, dict[str, Any]]] = []

    async def invoke_hook(self, hook_name: str, **kwargs: Any) -> SimpleNamespace:
        """模拟调用运行时命名 Hook。

        Args:
            hook_name: 目标 Hook 名称。
            **kwargs: 传入 Hook 的参数。

        Returns:
            SimpleNamespace: 预设的 Hook 返回结果。
        """

        self.calls.append((hook_name, dict(kwargs)))
        return self._responses.get(hook_name, SimpleNamespace(kwargs=dict(kwargs), aborted=False))


def test_builtin_hook_catalog_includes_new_business_hooks(monkeypatch: pytest.MonkeyPatch) -> None:
    """内置 Hook 目录应包含三个业务系统新增的 Hook。"""

    monkeypatch.setattr(sys, "exit", lambda code=0: None)
    from src.plugin_runtime.hook_catalog import register_builtin_hook_specs
    from src.plugin_runtime.host.hook_spec_registry import HookSpecRegistry

    registry = HookSpecRegistry()
    hook_names = {spec.name for spec in register_builtin_hook_specs(registry)}

    assert "emoji.maisaka.before_select" in hook_names
    assert "emoji.register.after_build_emotion" in hook_names
    assert "jargon.extract.before_persist" in hook_names
    assert "jargon.query.after_search" in hook_names
    assert "expression.select.before_select" in hook_names
    assert "expression.learn.before_upsert" in hook_names


@pytest.mark.asyncio
async def test_send_emoji_for_maisaka_can_be_aborted_by_hook(monkeypatch: pytest.MonkeyPatch) -> None:
    """表情包系统应允许在选择前被 Hook 中止。"""

    from src.emoji_system import maisaka_tool

    fake_manager = _FakeHookManager(
        {
            "emoji.maisaka.before_select": SimpleNamespace(
                kwargs={"abort_message": "插件阻止了表情发送。"},
                aborted=True,
            )
        }
    )
    monkeypatch.setattr(maisaka_tool, "_get_runtime_manager", lambda: fake_manager)

    result = await maisaka_tool.send_emoji_for_maisaka(stream_id="stream-1", requested_emotion="开心")

    assert result.success is False
    assert result.message == "插件阻止了表情发送。"
    assert fake_manager.calls[0][0] == "emoji.maisaka.before_select"


@pytest.mark.asyncio
async def test_jargon_extract_can_be_aborted_before_persist(monkeypatch: pytest.MonkeyPatch) -> None:
    """黑话提取结果应允许在写库前被 Hook 中止。"""

    from src.learners.jargon_miner import JargonMiner

    fake_manager = _FakeHookManager(
        {
            "jargon.extract.before_persist": SimpleNamespace(
                kwargs={"entries": []},
                aborted=True,
            )
        }
    )
    monkeypatch.setattr(JargonMiner, "_get_runtime_manager", staticmethod(lambda: fake_manager))

    miner = JargonMiner(session_id="session-1", session_name="测试会话")
    await miner.process_extracted_entries(
        [{"content": "yyds", "raw_content": {"[1] yyds 太强了"}}],
    )

    assert fake_manager.calls[0][0] == "jargon.extract.before_persist"
    assert fake_manager.calls[0][1]["session_id"] == "session-1"


@pytest.mark.asyncio
async def test_expression_selection_can_be_aborted_by_hook(monkeypatch: pytest.MonkeyPatch) -> None:
    """表达方式选择流程应允许在开始前被 Hook 中止。"""

    from src.learners.expression_selector import ExpressionSelector

    fake_manager = _FakeHookManager(
        {
            "expression.select.before_select": SimpleNamespace(
                kwargs={},
                aborted=True,
            )
        }
    )
    monkeypatch.setattr(ExpressionSelector, "_get_runtime_manager", staticmethod(lambda: fake_manager))
    monkeypatch.setattr(ExpressionSelector, "can_use_expression_for_chat", lambda self, chat_id: True)

    selector = ExpressionSelector()
    selected_expressions, selected_ids = await selector.select_suitable_expressions(
        chat_id="session-1",
        chat_info="用户刚刚发来一条消息。",
    )

    assert selected_expressions == []
    assert selected_ids == []
    assert fake_manager.calls[0][0] == "expression.select.before_select"
