"""End-to-end integration tests for TrustMem SDK.

Tests full lifecycle: add → search → get_all → verify → delete,
exercising the entire pipeline with mock AI.
"""
import pytest

from trustmem.sdk import TrustMem
from trustmem.handler.persistent import PersistentHandler
from trustmem.exceptions import NotFoundError, ValidationError


@pytest.fixture
def m(mock_ai):
    """TrustMem SDK with in-memory stores."""
    handler = PersistentHandler(database_url=":memory:", ai_client=mock_ai)
    return TrustMem._from_handler(handler)


class TestAddSearchDeleteLifecycle:
    """Full lifecycle: add → search → get_all → delete."""

    def test_add_then_search(self, m):
        """Added memory should be discoverable by search."""
        # add_memory directly (bypasses LLM extraction which returns empty in mock)
        m._handler.add_memory(
            user_id="e2e-agent",
            content="Alice lives in Tokyo",
            tags=["test"],
        )

        result = m.search("Tokyo", user_id="e2e-agent")
        assert "results" in result
        assert "relations" in result
        assert any("Tokyo" in r["content"] for r in result["results"])

    def test_add_then_get_all(self, m):
        """Added memory should appear in get_all results."""
        m._handler.add_memory(
            user_id="e2e-agent",
            content="Bob likes sushi",
            tags=["test"],
        )

        result = m.get_all(user_id="e2e-agent")
        assert "results" in result
        assert len(result["results"]) >= 1
        assert any("sushi" in r["content"] for r in result["results"])

    def test_delete_removes_memory(self, m):
        """Deleted memory should not appear in get_all."""
        res = m._handler.add_memory(
            user_id="e2e-agent",
            content="Temporary fact",
            tags=["test"],
        )
        memory_id = res["memory_id"]

        # Confirm it exists
        before = m.get_all(user_id="e2e-agent")
        assert any(r["memory_id"] == memory_id for r in before["results"])

        # Delete
        assert m.delete(memory_id) is True

        # Confirm it's gone
        after = m.get_all(user_id="e2e-agent")
        assert not any(r["memory_id"] == memory_id for r in after["results"])

    def test_delete_nonexistent_raises(self, m):
        with pytest.raises(NotFoundError):
            m.delete("nonexistent-id")


class TestVerifyLifecycle:
    """Verify and verify_all through SDK."""

    def test_verify_valid_memory(self, m):
        res = m._handler.add_memory(
            user_id="e2e-agent",
            content="Verified fact",
            tags=["test"],
        )
        result = m.verify(res["memory_id"])
        assert result["valid"] is True

    def test_verify_all_empty(self, m):
        result = m.verify_all()
        assert result["total"] == 0
        assert result["valid"] == 0

    def test_verify_all_with_data(self, m):
        m._handler.add_memory(
            user_id="e2e-agent",
            content="Fact one",
        )
        m._handler.add_memory(
            user_id="e2e-agent",
            content="Fact two",
        )
        result = m.verify_all()
        assert result["total"] == 2
        assert result["valid"] == 2


class TestStatsLifecycle:
    """Stats and entity_stats through SDK."""

    def test_stats_empty(self, m):
        result = m.stats(user_id="e2e-agent")
        assert "total_memories" in result

    def test_stats_after_add(self, m):
        m._handler.add_memory(
            user_id="e2e-agent",
            content="Stats test fact",
        )
        result = m.stats(user_id="e2e-agent")
        assert result["total_memories"] >= 1

    def test_entity_stats(self, m):
        result = m.entity_stats(user_id="e2e-agent")
        assert "total_entities" in result


class TestGraphLifecycle:
    """Knowledge graph operations through SDK."""

    def test_add_and_search_relation(self, m):
        m.add_relation(
            "Alice", "likes", "sushi",
            user_id="e2e-agent",
        )
        relations = m.search_relations("Alice", user_id="e2e-agent")
        assert len(relations) >= 1
        assert any(r["predicate"] == "likes" for r in relations)

    def test_get_graph(self, m):
        m.add_relation(
            "Bob", "works_at", "Google",
            user_id="e2e-agent",
        )
        graph = m.get_graph("Bob", user_id="e2e-agent")
        assert "nodes" in graph or "edges" in graph


class TestExceptionHierarchy:
    """Verify custom exceptions maintain backward compatibility."""

    def test_not_found_is_key_error(self, m):
        """NotFoundError should be caught by except KeyError."""
        with pytest.raises(KeyError):
            m.delete("nonexistent")

    def test_validation_error_is_value_error(self, m):
        """ValidationError should be caught by except ValueError."""
        with pytest.raises(ValueError):
            m.add("not a list")

    def test_validation_error_is_trustmem_error(self, m):
        """ValidationError should be caught by except TrustMemError."""
        from trustmem.exceptions import TrustMemError
        with pytest.raises(TrustMemError):
            m.add("not a list")


class TestContextManager:
    """Test with-statement lifecycle."""

    def test_with_statement(self, mock_ai):
        handler = PersistentHandler(database_url=":memory:", ai_client=mock_ai)
        with TrustMem._from_handler(handler) as tm:
            tm._handler.add_memory(
                content="Context manager test",
                user_id="default",
            )
            result = tm.search("test", user_id="default")
            assert "results" in result
