"""Tests for VidaMemoryBridge — 三层记忆接入 operator。

测试覆盖：
1. 初始化：正确创建三层服务（工作 / 情节 / 语义）
2. 异步写入：store_working / store_episodic / store_semantic 不阻塞事件循环
3. 异步检索：recall_working / recall_episodic / recall_semantic 返回正确结果
4. 持久化：persist_all 对三层分别调用 manager.persist
5. 配置透传：max_size / routing_strategy 等参数正确传递给服务
6. 关闭：close() 正常释放（不抛异常）
"""

from __future__ import annotations

import asyncio
from unittest.mock import MagicMock

import pytest

# ---------------------------------------------------------------------------
# Fixtures & helpers
# ---------------------------------------------------------------------------


def _make_mock_service(name: str = "mock_service") -> MagicMock:
    """创建模拟 BaseMemoryService 实例。"""
    svc = MagicMock(name=name)
    svc.insert.return_value = f"id_{name}"
    svc.retrieve.return_value = [{"id": f"id_{name}", "text": "sample", "metadata": {}}]
    return svc


@pytest.fixture()
def mock_manager():
    """模拟 MemoryManager。"""
    mgr = MagicMock(name="MemoryManager")
    mgr.create_collection.side_effect = lambda name, **kw: MagicMock(name=f"col_{name}")
    mgr.persist.return_value = True
    return mgr


@pytest.fixture()
def mock_services():
    """三层模拟服务 dict: layer -> MockService。"""
    return {
        "fifo_queue": _make_mock_service("working"),
        "feature_queue_summary_combination": _make_mock_service("episodic"),
        "semantic_inverted_knowledge_graph": _make_mock_service("semantic"),
    }


@pytest.fixture()
def bridge(mock_manager, mock_services):
    """Patched VidaMemoryBridge（三层服务均被 mock，绕过真实 neuromem 初始化）。

    使用 ``object.__new__`` 跳过 ``__init__``，直接注入 mock 属性，
    避免依赖真实 isage-neuromem 安装。
    """
    from sage.vida.vida_memory_bridge import VidaMemoryBridge

    obj = object.__new__(VidaMemoryBridge)
    obj._config = {}
    obj.manager = mock_manager
    obj.working_service = mock_services["fifo_queue"]
    obj.episodic_service = mock_services["feature_queue_summary_combination"]
    obj.semantic_service = mock_services["semantic_inverted_knowledge_graph"]
    return obj


# ---------------------------------------------------------------------------
# 初始化测试
# ---------------------------------------------------------------------------


class TestVidaMemoryBridgeInit:
    """测试 VidaMemoryBridge 三层服务初始化。"""

    def test_services_set_on_direct_instantiation(self, bridge):
        """三层服务属性应均不为 None。"""
        assert bridge.working_service is not None
        assert bridge.episodic_service is not None
        assert bridge.semantic_service is not None

    def test_manager_set(self, bridge, mock_manager):
        """MemoryManager 应正确赋值。"""
        assert bridge.manager is mock_manager


# ---------------------------------------------------------------------------
# 工作记忆测试
# ---------------------------------------------------------------------------


class TestWorkingMemory:
    """测试工作记忆（FIFO）的写入与检索。"""

    def test_store_working_calls_service_insert(self, bridge, mock_services):
        """store_working 应调用 working_service.insert(text, metadata)。"""
        asyncio.run(bridge.store_working("hello world", {"role": "user"}))
        mock_services["fifo_queue"].insert.assert_called_once_with("hello world", {"role": "user"})

    def test_store_working_returns_id(self, bridge):
        """store_working 应返回服务返回的 data_id。"""
        result = asyncio.run(bridge.store_working("text"))
        assert isinstance(result, str)

    def test_recall_working_calls_service_retrieve(self, bridge, mock_services):
        """recall_working 应调用 working_service.retrieve(query, top_k)。"""
        asyncio.run(bridge.recall_working("query", top_k=3))
        mock_services["fifo_queue"].retrieve.assert_called_once_with(query="query", top_k=3)

    def test_recall_working_returns_list(self, bridge):
        """recall_working 应返回列表。"""
        results = asyncio.run(bridge.recall_working())
        assert isinstance(results, list)

    def test_recall_working_empty_query_default(self, bridge, mock_services):
        """recall_working 在 query 为空时应正常工作（FIFO 按时序返回）。"""
        asyncio.run(bridge.recall_working())
        call_args = mock_services["fifo_queue"].retrieve.call_args
        assert call_args.kwargs.get("query") == "" or call_args.args[0:1] == ("",)


# ---------------------------------------------------------------------------
# 情节记忆测试
# ---------------------------------------------------------------------------


class TestEpisodicMemory:
    """测试情节记忆（摘要组合）的写入与检索。"""

    def test_store_episodic_calls_service_insert(self, bridge, mock_services):
        """store_episodic 应调用 episodic_service.insert。"""
        asyncio.run(bridge.store_episodic("今天发生了重要事件", {"event_type": "key"}))
        mock_services["feature_queue_summary_combination"].insert.assert_called_once_with(
            "今天发生了重要事件", {"event_type": "key"}
        )

    def test_recall_episodic_calls_service_retrieve(self, bridge, mock_services):
        """recall_episodic 应调用 episodic_service.retrieve。"""
        asyncio.run(bridge.recall_episodic("重要事件", top_k=5))
        mock_services["feature_queue_summary_combination"].retrieve.assert_called_once_with(
            query="重要事件", top_k=5
        )

    def test_recall_episodic_returns_list(self, bridge):
        """recall_episodic 应返回列表。"""
        results = asyncio.run(bridge.recall_episodic("test"))
        assert isinstance(results, list)


# ---------------------------------------------------------------------------
# 语义记忆测试
# ---------------------------------------------------------------------------


class TestSemanticMemory:
    """测试语义记忆（知识图谱）的写入与检索。"""

    def test_store_semantic_calls_service_insert(self, bridge, mock_services):
        """store_semantic 应调用 semantic_service.insert。"""
        asyncio.run(bridge.store_semantic("Paris is the capital of France", {"entity": "Paris"}))
        mock_services["semantic_inverted_knowledge_graph"].insert.assert_called_once_with(
            "Paris is the capital of France", {"entity": "Paris"}
        )

    def test_recall_semantic_calls_service_retrieve(self, bridge, mock_services):
        """recall_semantic 应调用 semantic_service.retrieve。"""
        asyncio.run(bridge.recall_semantic("capital of France", top_k=3))
        mock_services["semantic_inverted_knowledge_graph"].retrieve.assert_called_once_with(
            query="capital of France", top_k=3
        )

    def test_recall_semantic_passes_extra_kwargs(self, bridge, mock_services):
        """recall_semantic 应将 kwargs（如 routing_strategy）透传给服务。"""
        asyncio.run(bridge.recall_semantic("query", top_k=2, routing_strategy="parallel"))
        _, kwargs = mock_services["semantic_inverted_knowledge_graph"].retrieve.call_args
        assert kwargs.get("routing_strategy") == "parallel"


# ---------------------------------------------------------------------------
# 持久化测试
# ---------------------------------------------------------------------------


class TestPersistAll:
    """测试 persist_all 对三层分别持久化。"""

    def test_persist_all_calls_manager_three_times(self, bridge, mock_manager):
        """persist_all 应对三个 collection 名各调用一次 manager.persist。"""
        asyncio.run(bridge.persist_all())
        assert mock_manager.persist.call_count == 3

    def test_persist_all_uses_correct_collection_names(self, bridge, mock_manager):
        """persist_all 应使用正确的 collection 名称。"""
        asyncio.run(bridge.persist_all())
        called_names = {c.args[0] for c in mock_manager.persist.call_args_list}
        assert called_names == {
            "vida_working_memory",
            "vida_episodic_memory",
            "vida_semantic_memory",
        }

    def test_persist_all_returns_dict_with_three_keys(self, bridge):
        """persist_all 返回值应包含三层 key。"""
        result = asyncio.run(bridge.persist_all())
        assert set(result.keys()) == {"working_memory", "episodic_memory", "semantic_memory"}

    def test_persist_all_returns_bool_values(self, bridge):
        """persist_all 每层结果应为 bool。"""
        result = asyncio.run(bridge.persist_all())
        for v in result.values():
            assert isinstance(v, bool)


# ---------------------------------------------------------------------------
# 生命周期测试
# ---------------------------------------------------------------------------


class TestLifecycle:
    """测试 VidaMemoryBridge 生命周期。"""

    def test_close_does_not_raise(self, bridge):
        """close() 应正常执行，不抛出异常。"""
        asyncio.run(bridge.close())

    def test_multiple_operations_do_not_block(self, bridge):
        """并发执行多次异步操作不应互相阻塞（简单冒烟测试）。"""

        async def _run():
            await asyncio.gather(
                bridge.store_working("msg1"),
                bridge.store_working("msg2"),
                bridge.recall_working("query"),
            )

        asyncio.run(_run())
