"""Unit tests for the memory module."""

from __future__ import annotations

from pathlib import Path

import pytest

from core.database import Database
from core.memory import MemoryEntry, MemoryStore

# ---------------------------------------------------------------------------
# MemoryEntry
# ---------------------------------------------------------------------------


class TestMemoryEntry:
    """Tests for MemoryEntry dataclass."""

    def test_to_dict_and_from_dict_roundtrip(self) -> None:
        entry = MemoryEntry(
            id="abc123",
            content="User prefers dark mode",
            created_at="2026-02-04T10:00:00Z",
            source_session="session1",
            tags=["preference", "ui"],
        )

        data = entry.to_dict()
        restored = MemoryEntry.from_dict(data)

        assert restored.id == entry.id
        assert restored.content == entry.content
        assert restored.created_at == entry.created_at
        assert restored.source_session == entry.source_session
        assert restored.tags == entry.tags

    def test_from_dict_missing_tags(self) -> None:
        data = {
            "id": "abc",
            "content": "Test",
            "created_at": "2026-02-04T10:00:00Z",
            "source_session": "s1",
        }
        entry = MemoryEntry.from_dict(data)
        assert entry.tags == []


# ---------------------------------------------------------------------------
# MemoryStore
# ---------------------------------------------------------------------------


class TestMemoryStore:
    """Tests for MemoryStore class."""

    @pytest.fixture
    def store(self, tmp_path: Path) -> MemoryStore:
        """Create a memory store backed by a temp database."""
        db = Database(tmp_path / "test.db")
        return MemoryStore(db)

    def test_add_creates_entry(self, store: MemoryStore) -> None:
        entry = store.add("User likes Python", "session1")

        assert entry.id
        assert entry.content == "User likes Python"
        assert entry.source_session == "session1"
        assert entry.tags == []

    def test_add_with_tags(self, store: MemoryStore) -> None:
        entry = store.add("Prefers vim", "session1", tags=["editor", "preference"])
        assert entry.tags == ["editor", "preference"]

    def test_add_persists_to_database(self, tmp_path: Path) -> None:
        db = Database(tmp_path / "test.db")
        store = MemoryStore(db)
        store.add("Persistent fact", "session1")

        # Create new store from same database
        store2 = MemoryStore(db)
        memories = store2.list_all()

        assert len(memories) == 1
        assert memories[0].content == "Persistent fact"

    def test_remove_existing(self, store: MemoryStore) -> None:
        entry = store.add("To be removed", "session1")

        result = store.remove(entry.id)
        assert result is True

        memories = store.list_all()
        assert len(memories) == 0

    def test_remove_nonexistent(self, store: MemoryStore) -> None:
        result = store.remove("nonexistent")
        assert result is False

    def test_list_all_empty(self, store: MemoryStore) -> None:
        memories = store.list_all()
        assert memories == []

    def test_list_all_sorted_newest_first(self, store: MemoryStore) -> None:
        store.add("First", "s1")
        store.add("Second", "s1")
        store.add("Third", "s1")

        memories = store.list_all()
        assert len(memories) == 3
        # Newest should be first
        assert memories[0].content == "Third"
        assert memories[2].content == "First"

    def test_search_matches_content(self, store: MemoryStore) -> None:
        store.add("User prefers Python", "s1")
        store.add("Project uses JavaScript", "s1")
        store.add("Data stored in ./data/", "s1")

        results = store.search("python")
        assert len(results) == 1
        assert results[0].content == "User prefers Python"

    def test_search_case_insensitive(self, store: MemoryStore) -> None:
        store.add("User prefers PYTHON", "s1")

        results = store.search("python")
        assert len(results) == 1

    def test_search_matches_tags(self, store: MemoryStore) -> None:
        store.add("Some preference", "s1", tags=["editor", "vim"])
        store.add("Another fact", "s1", tags=["database"])

        results = store.search("vim")
        assert len(results) == 1
        assert results[0].tags == ["editor", "vim"]

    def test_search_no_results(self, store: MemoryStore) -> None:
        store.add("User prefers Python", "s1")

        results = store.search("rust")
        assert len(results) == 0

    def test_get_context_string_empty(self, store: MemoryStore) -> None:
        context = store.get_context_string()
        assert context == ""

    def test_get_context_string_with_memories(self, store: MemoryStore) -> None:
        store.add("User name is Alice", "s1")
        store.add("Prefers dark mode", "s1")

        context = store.get_context_string()

        assert "## Things you remember about this user" in context
        assert "User name is Alice" in context
        assert "Prefers dark mode" in context

    def test_get_context_string_respects_limit(self, store: MemoryStore) -> None:
        for i in range(10):
            store.add(f"Memory {i}", "s1")

        context = store.get_context_string(limit=3)

        # Should only have 3 memories (plus the header line)
        lines = [line for line in context.split("\n") if line.startswith("- ")]
        assert len(lines) == 3
