import pytest

from src.services.memory_service import MemorySearchResult, MemoryService


def test_coerce_write_result_treats_skipped_payload_as_success():
    result = MemoryService._coerce_write_result({"skipped_ids": ["p1"], "detail": "chat_filtered"})

    assert result.success is True
    assert result.stored_ids == []
    assert result.skipped_ids == ["p1"]
    assert result.detail == "chat_filtered"


@pytest.mark.asyncio
async def test_graph_admin_invokes_plugin(monkeypatch):
    service = MemoryService()
    calls = []

    async def fake_invoke(component_name, args=None, **kwargs):
        calls.append((component_name, args, kwargs))
        return {"success": True, "nodes": [], "edges": []}

    monkeypatch.setattr(service, "_invoke", fake_invoke)

    result = await service.graph_admin(action="get_graph", limit=12)

    assert result["success"] is True
    assert calls == [("memory_graph_admin", {"action": "get_graph", "limit": 12}, {"timeout_ms": 30000})]


@pytest.mark.asyncio
async def test_get_recycle_bin_uses_maintain_memory_tool(monkeypatch):
    service = MemoryService()
    calls = []

    async def fake_invoke(component_name, args=None, **kwargs):
        calls.append((component_name, args))
        return {"success": True, "items": [{"hash": "abc"}], "count": 1}

    monkeypatch.setattr(service, "_invoke", fake_invoke)

    result = await service.get_recycle_bin(limit=5)

    assert result == {"success": True, "items": [{"hash": "abc"}], "count": 1}
    assert calls == [("maintain_memory", {"action": "recycle_bin", "limit": 5})]


@pytest.mark.asyncio
async def test_search_respects_filter_by_default(monkeypatch):
    service = MemoryService()
    calls = []

    async def fake_invoke(component_name, args=None, **kwargs):
        calls.append((component_name, args))
        return {"summary": "ok", "hits": [], "filtered": True}

    monkeypatch.setattr(service, "_invoke", fake_invoke)

    result = await service.search(
        "mai",
        chat_id="stream-1",
        person_id="person-1",
        user_id="user-1",
        group_id="",
    )

    assert isinstance(result, MemorySearchResult)
    assert result.filtered is True
    assert calls == [
        (
            "search_memory",
            {
                "query": "mai",
                "limit": 5,
                "mode": "search",
                "chat_id": "stream-1",
                "person_id": "person-1",
                "time_start": None,
                "time_end": None,
                "respect_filter": True,
                "user_id": "user-1",
                "group_id": "",
            },
        )
    ]


@pytest.mark.asyncio
async def test_ingest_summary_can_bypass_filter(monkeypatch):
    service = MemoryService()
    calls = []

    async def fake_invoke(component_name, args=None, **kwargs):
        calls.append((component_name, args))
        return {"success": True, "stored_ids": ["p1"], "detail": ""}

    monkeypatch.setattr(service, "_invoke", fake_invoke)

    result = await service.ingest_summary(
        external_id="chat_history:1",
        chat_id="stream-1",
        text="summary",
        respect_filter=False,
        user_id="user-1",
    )

    assert result.success is True
    assert calls == [
        (
            "ingest_summary",
            {
                "external_id": "chat_history:1",
                "chat_id": "stream-1",
                "text": "summary",
                "participants": [],
                "time_start": None,
                "time_end": None,
                "tags": [],
                "metadata": {},
                "respect_filter": False,
                "user_id": "user-1",
                "group_id": "",
            },
        )
    ]


@pytest.mark.asyncio
async def test_v5_admin_invokes_plugin(monkeypatch):
    service = MemoryService()
    calls = []

    async def fake_invoke(component_name, args=None, **kwargs):
        calls.append((component_name, args, kwargs))
        return {"success": True, "count": 1}

    monkeypatch.setattr(service, "_invoke", fake_invoke)

    result = await service.v5_admin(action="status", target="mai", limit=5)

    assert result["success"] is True
    assert calls == [("memory_v5_admin", {"action": "status", "target": "mai", "limit": 5}, {"timeout_ms": 30000})]


@pytest.mark.asyncio
async def test_delete_admin_uses_long_timeout(monkeypatch):
    service = MemoryService()
    calls = []

    async def fake_invoke(component_name, args=None, **kwargs):
        calls.append((component_name, args, kwargs))
        return {"success": True, "operation_id": "del-1"}

    monkeypatch.setattr(service, "_invoke", fake_invoke)

    result = await service.delete_admin(action="execute", mode="relation", selector={"query": "mai"})

    assert result["success"] is True
    assert calls == [
        (
            "memory_delete_admin",
            {"action": "execute", "mode": "relation", "selector": {"query": "mai"}},
            {"timeout_ms": 120000},
        )
    ]


@pytest.mark.asyncio
async def test_search_returns_empty_when_query_and_time_missing_async():
    service = MemoryService()

    result = await service.search("", time_start=None, time_end=None)

    assert isinstance(result, MemorySearchResult)
    assert result.summary == ""
    assert result.hits == []
    assert result.filtered is False


@pytest.mark.asyncio
async def test_search_accepts_string_time_bounds(monkeypatch):
    service = MemoryService()
    calls = []

    async def fake_invoke(component_name, args=None, **kwargs):
        calls.append((component_name, args))
        return {"summary": "ok", "hits": [], "filtered": False}

    monkeypatch.setattr(service, "_invoke", fake_invoke)

    result = await service.search(
        "广播站",
        mode="time",
        time_start="2026/03/18",
        time_end="2026/03/18 09:30",
    )

    assert isinstance(result, MemorySearchResult)
    assert calls == [
        (
            "search_memory",
            {
                "query": "广播站",
                "limit": 5,
                "mode": "time",
                "chat_id": "",
                "person_id": "",
                "time_start": "2026/03/18",
                "time_end": "2026/03/18 09:30",
                "respect_filter": True,
                "user_id": "",
                "group_id": "",
            },
        )
    ]


def test_coerce_search_result_preserves_aggregate_source_branches():
    result = MemoryService._coerce_search_result(
        {
            "hits": [
                {
                    "content": "广播站值夜班",
                    "type": "paragraph",
                    "metadata": {"event_time_start": 1.0},
                    "source_branches": ["search", "time"],
                    "rank": 1,
                }
            ]
        }
    )

    assert result.hits[0].metadata["source_branches"] == ["search", "time"]
    assert result.hits[0].metadata["rank"] == 1


@pytest.mark.asyncio
async def test_import_admin_uses_long_timeout(monkeypatch):
    service = MemoryService()
    calls = []

    async def fake_invoke(component_name, args=None, **kwargs):
        calls.append((component_name, args, kwargs))
        return {"success": True, "task_id": "import-1"}

    monkeypatch.setattr(service, "_invoke", fake_invoke)

    result = await service.import_admin(action="create_lpmm_openie", alias="lpmm")

    assert result["success"] is True
    assert calls == [
        (
            "memory_import_admin",
            {"action": "create_lpmm_openie", "alias": "lpmm"},
            {"timeout_ms": 120000},
        )
    ]


@pytest.mark.asyncio
async def test_tuning_admin_uses_long_timeout(monkeypatch):
    service = MemoryService()
    calls = []

    async def fake_invoke(component_name, args=None, **kwargs):
        calls.append((component_name, args, kwargs))
        return {"success": True, "task_id": "tuning-1"}

    monkeypatch.setattr(service, "_invoke", fake_invoke)

    result = await service.tuning_admin(action="create_task", payload={"query": "mai"})

    assert result["success"] is True
    assert calls == [
        (
            "memory_tuning_admin",
            {"action": "create_task", "payload": {"query": "mai"}},
            {"timeout_ms": 120000},
        )
    ]
