"""
Tests for Feature 5: Persistent Memory.
  - save_fact / list_facts / delete_fact / update_fact
  - save_message / get_session_history
  - search_memory (semantic + SQLite fallback)
  - get_stats / get_facts_summary
"""

import pytest


class TestFacts:
    """SQLite-backed fact CRUD."""

    async def test_save_fact_returns_integer_id(self, memory_store):
        fact_id = memory_store.save_fact("User works at Acme Corp", category="work")
        assert isinstance(fact_id, int)
        assert fact_id > 0

    async def test_list_facts_returns_saved_facts(self, memory_store):
        memory_store.save_fact("User likes Python", category="preferences")
        memory_store.save_fact("User lives in Berlin", category="personal")

        facts = memory_store.list_facts()
        assert len(facts) >= 2
        fact_texts = [f["fact"] for f in facts]
        assert any("Python" in t for t in fact_texts)
        assert any("Berlin" in t for t in fact_texts)

    async def test_list_facts_filter_by_category(self, memory_store):
        memory_store.save_fact("User is a developer", category="work")
        memory_store.save_fact("User enjoys hiking", category="hobbies")

        work_facts = memory_store.list_facts(category="work")
        assert all(f["category"] == "work" for f in work_facts)
        assert any("developer" in f["fact"] for f in work_facts)

    async def test_delete_fact_removes_it_from_list(self, memory_store):
        fact_id = memory_store.save_fact("Temporary fact", category="test")
        assert memory_store.delete_fact(fact_id) is True

        facts = memory_store.list_facts()
        ids = [f["id"] for f in facts]
        assert fact_id not in ids

    async def test_delete_nonexistent_fact_returns_false(self, memory_store):
        result = memory_store.delete_fact(99999)
        assert result is False

    async def test_fact_persists_after_reinitialize(self, memory_config):
        """Facts written to SQLite survive close + re-open."""
        from duckclaw.memory.store import MemoryStore

        store1 = MemoryStore(memory_config)
        await store1.initialize()
        fact_id = store1.save_fact("Persistent fact across sessions", category="test")
        store1.close()

        store2 = MemoryStore(memory_config)
        await store2.initialize()
        facts = store2.list_facts()
        store2.close()

        ids = [f["id"] for f in facts]
        assert fact_id in ids

    async def test_get_facts_summary_returns_formatted_string(self, memory_store):
        memory_store.save_fact("User prefers dark mode", category="preferences")
        memory_store.save_fact("User is a backend engineer", category="work")

        summary = memory_store.get_facts_summary()
        assert isinstance(summary, str)
        assert len(summary) > 0
        assert "preferences" in summary.lower() or "work" in summary.lower()

    async def test_get_facts_summary_empty_when_no_facts(self, memory_store):
        summary = memory_store.get_facts_summary()
        assert summary == ""


class TestConversationHistory:
    """Message persistence and session isolation."""

    async def test_save_message_returns_integer_id(self, memory_store):
        msg_id = memory_store.save_message("sess-1", "user", "Hello DuckClaw!", "terminal")
        assert isinstance(msg_id, int)
        assert msg_id > 0

    async def test_get_session_history_returns_correct_order(self, memory_store):
        memory_store.save_message("sess-order", "user", "First message", "terminal")
        memory_store.save_message("sess-order", "assistant", "First reply", "terminal")
        memory_store.save_message("sess-order", "user", "Second message", "terminal")

        history = memory_store.get_session_history("sess-order")
        assert len(history) == 3
        # Verify all messages are present (timestamp-based ordering has second precision)
        contents = [m["content"] for m in history]
        assert "First message" in contents
        assert "First reply" in contents
        assert "Second message" in contents
        roles = [m["role"] for m in history]
        assert roles.count("user") == 2
        assert roles.count("assistant") == 1

    async def test_get_session_history_isolates_sessions(self, memory_store):
        memory_store.save_message("sess-A", "user", "Message in A", "terminal")
        memory_store.save_message("sess-B", "user", "Message in B", "terminal")

        history_a = memory_store.get_session_history("sess-A")
        assert all(m["content"] != "Message in B" for m in history_a)

    async def test_get_session_history_respects_limit(self, memory_store):
        for i in range(10):
            memory_store.save_message("sess-limit", "user", f"Message {i}", "terminal")

        history = memory_store.get_session_history("sess-limit", limit=5)
        assert len(history) <= 5


class TestSemanticSearch:
    """search_memory uses ChromaDB (or SQLite fallback)."""

    async def test_search_memory_returns_relevant_result(self, memory_store):
        memory_store.save_message("sess-search", "user", "I work as a Python developer at a startup", "terminal")
        memory_store.save_message("sess-search", "assistant", "That's great! Python is a great language.", "terminal")

        results = memory_store.search_memory("Python developer", n_results=3)
        assert isinstance(results, list)
        # At least one result should be relevant (ChromaDB or SQLite fallback)
        assert len(results) >= 1
        assert any("Python" in r for r in results)

    async def test_search_memory_returns_empty_list_when_no_messages(self, memory_store):
        results = memory_store.search_memory("completely unrelated query xyz")
        assert isinstance(results, list)


class TestMemoryStats:
    """get_stats() reflects actual stored data counts."""

    async def test_stats_counts_facts_and_messages(self, memory_store):
        memory_store.save_fact("Fact one", category="test")
        memory_store.save_fact("Fact two", category="test")
        memory_store.save_message("sess-stats", "user", "Hello", "terminal")

        stats = memory_store.get_stats()
        assert isinstance(stats, dict)
        assert stats["total_facts"] >= 2
        assert stats["total_messages"] >= 1
        assert "total_sessions" in stats
        assert "semantic_index_size" in stats
