"""VectorStore ABC compliance tests.

Verifies that SQLiteVectorStore implements the VectorStore interface
correctly.
"""

import pytest

from trustmem.storage.sqlite_store import SQLiteStore
from trustmem.vectorstore.base import VectorStore
from trustmem.vectorstore.sqlite import SQLiteVectorStore


# ============================================================
# Protocol / ABC compliance
# ============================================================


class TestABCCompliance:
    def test_is_subclass(self):
        assert issubclass(SQLiteVectorStore, VectorStore)

    def test_instance(self):
        store = SQLiteStore(":memory:")
        vs = SQLiteVectorStore(store)
        assert isinstance(vs, VectorStore)


# ============================================================
# CRUD operations
# ============================================================


@pytest.fixture
def vs():
    store = SQLiteStore(":memory:")
    return SQLiteVectorStore(store)


class TestUpsert:
    def test_upsert_and_count(self, vs):
        vs.upsert("id-1", [1.0, 0.0, 0.0], {"label": "first"})
        assert vs.count() == 1

    def test_upsert_overwrites(self, vs):
        vs.upsert("id-1", [1.0, 0.0, 0.0], {"label": "v1"})
        vs.upsert("id-1", [0.0, 1.0, 0.0], {"label": "v2"})
        assert vs.count() == 1
        results = vs.search([0.0, 1.0, 0.0], limit=1)
        assert results[0]["payload"]["label"] == "v2"


class TestSearch:
    def test_search_empty(self, vs):
        results = vs.search([1.0, 0.0, 0.0])
        assert results == []

    def test_search_returns_sorted(self, vs):
        vs.upsert("a", [1.0, 0.0, 0.0], {"name": "a"})
        vs.upsert("b", [0.0, 1.0, 0.0], {"name": "b"})
        vs.upsert("c", [0.7, 0.7, 0.0], {"name": "c"})

        results = vs.search([1.0, 0.0, 0.0], limit=3)
        # "a" should be first (exact match)
        assert results[0]["id"] == "a"
        assert results[0]["score"] == pytest.approx(1.0, abs=0.01)

    def test_search_limit(self, vs):
        for i in range(10):
            vs.upsert(f"id-{i}", [float(i), 1.0, 0.0], {"i": i})
        results = vs.search([5.0, 1.0, 0.0], limit=3)
        assert len(results) == 3

    def test_search_with_filter(self, vs):
        vs.upsert("a", [1.0, 0.0, 0.0], {"group": "x"})
        vs.upsert("b", [0.9, 0.1, 0.0], {"group": "y"})
        results = vs.search([1.0, 0.0, 0.0], limit=10, filters={"group": "y"})
        assert len(results) == 1
        assert results[0]["id"] == "b"


class TestDelete:
    def test_delete_existing(self, vs):
        vs.upsert("id-1", [1.0, 0.0], {"label": "del"})
        vs.delete("id-1")
        assert vs.count() == 0

    def test_delete_nonexistent(self, vs):
        # Should not raise
        vs.delete("nonexistent")


class TestUpdatePayload:
    def test_update_merges(self, vs):
        vs.upsert("id-1", [1.0, 0.0], {"a": 1, "b": 2})
        vs.update_payload("id-1", {"b": 20, "c": 3})
        results = vs.search([1.0, 0.0], limit=1)
        payload = results[0]["payload"]
        assert payload["a"] == 1
        assert payload["b"] == 20
        assert payload["c"] == 3

    def test_update_nonexistent_no_error(self, vs):
        # Should not raise
        vs.update_payload("nonexistent", {"x": 1})


class TestCosineEdgeCases:
    def test_zero_vector(self, vs):
        vs.upsert("id-1", [1.0, 0.0], {"x": 1})
        results = vs.search([0.0, 0.0], limit=1)
        assert results[0]["score"] == pytest.approx(0.0)

    def test_identical_vectors(self, vs):
        vs.upsert("id-1", [0.5, 0.5], {"x": 1})
        results = vs.search([0.5, 0.5], limit=1)
        assert results[0]["score"] == pytest.approx(1.0, abs=0.01)

    def test_opposite_vectors(self, vs):
        vs.upsert("id-1", [1.0, 0.0], {"x": 1})
        results = vs.search([-1.0, 0.0], limit=1)
        assert results[0]["score"] == pytest.approx(-1.0, abs=0.01)
