"""Tests for the persistent memory system."""

import pytest

from agent_scout.db.database import Database
from agent_scout.events import Event, EventBus, EventType
from agent_scout.memory.store import MemoryStore


@pytest.fixture
async def setup(tmp_path):
    db = Database(str(tmp_path / "test.db"))
    await db.connect()
    bus = EventBus()
    store = MemoryStore(db, bus)
    yield store, db, bus
    await db.close()


async def test_remember_and_recall(setup):
    store, db, bus = setup

    await store.remember(
        content="User prefers morning flights",
        memory_type="preference",
        tags=["flights", "timing"],
    )

    memories = await store.recall(memory_type="preference")
    assert len(memories) == 1
    assert memories[0].content == "User prefers morning flights"


async def test_recall_by_tags(setup):
    store, db, bus = setup

    await store.remember("Kayak has best prices", "strategy", tags=["flights", "source"])
    await store.remember("User likes window seats", "preference", tags=["flights", "seating"])
    await store.remember("Amazon Prime Day is in July", "fact", tags=["shopping", "deals"])

    flight_memories = await store.recall(tags=["flights"])
    assert len(flight_memories) == 2

    shopping_memories = await store.recall(tags=["shopping"])
    assert len(shopping_memories) == 1


async def test_recall_updates_access_count(setup):
    store, db, bus = setup

    m = await store.remember("test memory", "fact", tags=["test"])
    assert m.access_count == 0

    recalled = await store.recall(tags=["test"])
    assert len(recalled) == 1
    # access_count was updated in DB; need to re-fetch to see it
    await store.recall(tags=["test"])
    # After two recall calls, access count should be 2
    fresh = await db.search_memories(tags=["test"])
    assert fresh[0].access_count == 2


async def test_recall_emits_event(setup):
    store, db, bus = setup
    received = []

    async def handler(event: Event):
        received.append(event)

    bus.subscribe(handler, EventType.MEMORY_RECALLED)

    await store.remember("test", "fact", tags=["test"])
    await store.recall(tags=["test"])

    assert len(received) == 1
    assert received[0].data["count"] == 1


async def test_remember_emits_event(setup):
    store, db, bus = setup
    received = []

    async def handler(event: Event):
        received.append(event)

    bus.subscribe(handler, EventType.MEMORY_STORED)

    await store.remember("test memory", "preference", tags=["test"])
    assert len(received) == 1
    assert received[0].data["type"] == "preference"


async def test_decay_reduces_relevance(setup):
    store, db, bus = setup

    await store.remember("test", "fact", tags=["test"], relevance_score=1.0)
    await store.decay(factor=0.5)

    memories = await store.recall(tags=["test"])
    assert memories[0].relevance_score == pytest.approx(0.5, abs=0.01)


async def test_recall_for_task(setup):
    store, db, bus = setup

    await store.remember(
        "Kayak works well for flight searches",
        "strategy",
        tags=["kayak", "flights", "search"],
    )
    await store.remember(
        "Amazon is best for electronics",
        "strategy",
        tags=["amazon", "electronics", "shopping"],
    )

    results = await store.recall_for_task("Find cheap flights from NYC")
    assert len(results) >= 1
    assert any("Kayak" in m.content for m in results)


async def test_recall_for_task_empty_description(setup):
    store, db, bus = setup

    results = await store.recall_for_task("hi")
    assert results == []
