"""Performance tests for search/add latency.

Validates that basic memory operations meet latency targets under realistic
data volumes (CLAUDE.md: P99=750ms, P50=300ms).
"""

import time

import pytest

from conftest import MockAIClient
from trustmem.handler.persistent import PersistentHandler


@pytest.fixture
def handler():
    ai = MockAIClient()
    h = PersistentHandler(database_url=":memory:", ai_client=ai)
    h.reset_stores()
    return h


def _add_n_memories(handler, user_id: str, n: int):
    """Add n memories and return their IDs."""
    ids = []
    for i in range(n):
        r = handler.add_memory(f"Memory content number {i}", user_id=user_id)
        ids.append(r["memory_id"])
    return ids


class TestAddMemoryPerformance:
    """Verify add_memory latency."""

    def test_add_memory_returns_expected_fields(self, handler):
        """add_memory returns memory_id, content_hash, confidence, embedding_status."""
        r = handler.add_memory("Simple fact for perf test", user_id="test")
        assert "memory_id" in r
        assert "content_hash" in r
        assert "confidence" in r
        assert "embedding_status" in r

    def test_single_add_latency(self, handler):
        """Single add_memory should complete within 50ms."""
        start = time.perf_counter()
        handler.add_memory("Simple fact for perf test", user_id="test")
        elapsed = time.perf_counter() - start

        assert elapsed < 0.050, f"Single add took {elapsed * 1000:.2f}ms (target < 50ms)"

    def test_bulk_add_100_latency(self, handler):
        """Adding 100 memories should complete within 2s."""
        start = time.perf_counter()
        _add_n_memories(handler, "agent-perf", 100)
        elapsed = time.perf_counter() - start

        assert elapsed < 2.0, f"Bulk add 100 memories took {elapsed * 1000:.1f}ms (target < 2000ms)"


class TestSearchMemoryPerformance:
    """Verify search_memory latency under various data volumes."""

    def test_search_10_results_latency(self, handler):
        """search_memory with 10 results should complete within 750ms (P99 target)."""
        _add_n_memories(handler, "agent-perf", 50)

        start = time.perf_counter()
        result = handler.search_memory("Memory content", limit=10, user_id="agent-perf")
        elapsed = time.perf_counter() - start

        assert result["count"] > 0
        assert elapsed < 0.750, f"Search took {elapsed * 1000:.1f}ms (P99 target: 750ms)"

    def test_search_result_fields(self, handler):
        """search_memory results contain expected fields."""
        _add_n_memories(handler, "agent-perf", 5)
        result = handler.search_memory("Memory content", limit=5, user_id="agent-perf")

        assert "results" in result
        assert "count" in result
        for item in result["results"]:
            assert "memory_id" in item
            assert "content" in item
            assert "similarity" in item
            assert "confidence" in item
            assert "tags" in item
            assert "created_at" in item
            assert "last_evaluated_at" in item
            assert "freshness" in item

    def test_search_100_memories_latency(self, handler):
        """search_memory over 100 stored memories should stay within target."""
        _add_n_memories(handler, "agent-perf", 100)

        start = time.perf_counter()
        result = handler.search_memory("Memory content", limit=10, user_id="agent-perf")
        elapsed = time.perf_counter() - start

        assert result["count"] > 0
        assert elapsed < 0.750, f"Search over 100 memories took {elapsed * 1000:.1f}ms"

    def test_search_500_memories_latency(self, handler):
        """search_memory over 500 stored memories — stress test."""
        _add_n_memories(handler, "agent-perf", 500)

        start = time.perf_counter()
        result = handler.search_memory("Memory content", limit=10, user_id="agent-perf")
        elapsed = time.perf_counter() - start

        assert result["count"] > 0
        assert elapsed < 2.0, f"Search over 500 memories took {elapsed * 1000:.1f}ms"

    def test_search_p50_latency(self, handler):
        """Median search latency over 5 runs should be under 300ms (P50 target)."""
        _add_n_memories(handler, "agent-perf", 100)

        timings = []
        for _ in range(5):
            start = time.perf_counter()
            handler.search_memory("Memory content", limit=10, user_id="agent-perf")
            timings.append(time.perf_counter() - start)

        timings.sort()
        median = timings[len(timings) // 2]
        assert median < 0.300, f"Median search latency: {median * 1000:.1f}ms (P50 target: 300ms)"

    def test_search_repeated_is_consistent(self, handler):
        """Running search twice returns consistent results."""
        _add_n_memories(handler, "agent-perf", 50)

        result1 = handler.search_memory("Memory content", limit=10, user_id="agent-perf")
        result2 = handler.search_memory("Memory content", limit=10, user_id="agent-perf")

        assert result1["count"] == result2["count"]
        ids1 = [r["memory_id"] for r in result1["results"]]
        ids2 = [r["memory_id"] for r in result2["results"]]
        assert ids1 == ids2


class TestListMemoriesPerformance:
    """Verify list_memories latency."""

    def test_list_memories_latency(self, handler):
        """list_memories for 100 records should complete within 200ms."""
        _add_n_memories(handler, "agent-perf", 100)

        start = time.perf_counter()
        result = handler.list_memories(user_id="agent-perf", limit=50, offset=0)
        elapsed = time.perf_counter() - start

        assert result["count"] > 0
        assert elapsed < 0.200, f"list_memories took {elapsed * 1000:.1f}ms (target < 200ms)"

    def test_list_memories_pagination(self, handler):
        """list_memories offset pagination returns non-overlapping results."""
        _add_n_memories(handler, "agent-perf", 30)

        page1 = handler.list_memories(user_id="agent-perf", limit=10, offset=0)
        page2 = handler.list_memories(user_id="agent-perf", limit=10, offset=10)

        ids1 = {m["memory_id"] for m in page1["memories"]}
        ids2 = {m["memory_id"] for m in page2["memories"]}
        assert ids1.isdisjoint(ids2), "Pages should not overlap"
