"""Tests for vstash.store — VstashStore CRUD and RRF search."""

from __future__ import annotations

from tests.conftest import requires_sqlite_vec
from vstash.models import DocumentInfo, SearchResult, StoreStats
from vstash.store import VstashStore

pytestmark = requires_sqlite_vec


class TestStoreContextManager:
    """Test context manager protocol."""

    def test_context_manager_opens_and_closes(self, tmp_db_path: str) -> None:
        with VstashStore(tmp_db_path, embedding_dim=384) as store:
            assert store is not None
            s = store.stats()
            assert s.documents == 0

    def test_context_manager_returns_self(self, tmp_db_path: str) -> None:
        store = VstashStore(tmp_db_path, embedding_dim=384)
        with store as s:
            assert s is store


class TestStoreCRUD:
    """Test document CRUD operations."""

    def test_add_document(self, sample_store: VstashStore) -> None:
        dim = sample_store.embedding_dim
        doc_id = sample_store.add_document(
            path="/test/file.md",
            title="Test File",
            chunks=["chunk one", "chunk two"],
            embeddings=[[0.1] * dim, [0.2] * dim],
            source_type="markdown",
        )
        assert isinstance(doc_id, str)
        assert len(doc_id) == 32

    def test_doc_exists(self, sample_store: VstashStore) -> None:
        dim = sample_store.embedding_dim
        sample_store.add_document(
            path="/test/existing.md",
            title="Existing",
            chunks=["text"],
            embeddings=[[0.1] * dim],
        )
        assert sample_store.doc_exists("/test/existing.md") is True
        assert sample_store.doc_exists("/test/nonexistent.md") is False

    def test_list_documents(self, populated_store: VstashStore) -> None:
        docs = populated_store.list_documents()
        assert len(docs) == 2
        assert all(isinstance(d, DocumentInfo) for d in docs)
        titles = {d.title for d in docs}
        assert "Python Guide" in titles
        assert "ML Introduction" in titles

    def test_stats(self, populated_store: VstashStore) -> None:
        s = populated_store.stats()
        assert isinstance(s, StoreStats)
        assert s.documents == 2
        assert s.chunks == 5  # 2 + 3
        assert s.db_size_mb >= 0

    def test_delete_document(self, populated_store: VstashStore) -> None:
        deleted = populated_store.delete_document("/test/python_guide.md")
        assert deleted is True
        docs = populated_store.list_documents()
        assert len(docs) == 1
        assert docs[0].title == "ML Introduction"

    def test_delete_nonexistent_returns_false(self, sample_store: VstashStore) -> None:
        deleted = sample_store.delete_document("/nonexistent/file.md")
        assert deleted is False

    def test_re_ingest_replaces_document(self, sample_store: VstashStore) -> None:
        dim = sample_store.embedding_dim
        # First ingestion
        sample_store.add_document(
            path="/test/doc.md",
            title="V1",
            chunks=["version one"],
            embeddings=[[0.1] * dim],
        )
        # Second ingestion (same path)
        sample_store.add_document(
            path="/test/doc.md",
            title="V2",
            chunks=["version two", "extra chunk"],
            embeddings=[[0.2] * dim, [0.3] * dim],
        )
        docs = sample_store.list_documents()
        assert len(docs) == 1
        assert docs[0].title == "V2"
        assert docs[0].chunk_count == 2


class TestStoreSearch:
    """Test hybrid search and RRF ranking."""

    def test_search_returns_search_results(self, populated_store: VstashStore) -> None:
        dim = populated_store.embedding_dim
        query_vec = [0.1] * dim
        results = populated_store.search(query_vec, "Python programming")
        assert all(isinstance(r, SearchResult) for r in results)

    def test_search_respects_top_k(self, populated_store: VstashStore) -> None:
        dim = populated_store.embedding_dim
        query_vec = [0.1] * dim
        results = populated_store.search(query_vec, "Python", top_k=2)
        assert len(results) <= 2

    def test_search_returns_scores(self, populated_store: VstashStore) -> None:
        dim = populated_store.embedding_dim
        query_vec = [0.3] * dim
        results = populated_store.search(query_vec, "machine learning")
        if results:
            assert all(r.score > 0 for r in results)

    def test_empty_store_search(self, sample_store: VstashStore) -> None:
        dim = sample_store.embedding_dim
        query_vec = [0.1] * dim
        results = sample_store.search(query_vec, "anything")
        assert results == []

    def test_search_result_has_all_fields(self, populated_store: VstashStore) -> None:
        dim = populated_store.embedding_dim
        query_vec = [0.1] * dim
        results = populated_store.search(query_vec, "Python")
        if results:
            r = results[0]
            assert r.text
            assert r.title
            assert r.path
            assert isinstance(r.chunk, int)
            assert isinstance(r.score, float)


class TestStoreCollections:
    """Test collection-scoped operations."""

    def test_add_to_named_collection(self, sample_store: VstashStore) -> None:
        dim = sample_store.embedding_dim
        sample_store.add_document(
            path="/test/file.md",
            title="Test",
            chunks=["hello"],
            embeddings=[[0.1] * dim],
            collection="work",
        )
        docs = sample_store.list_documents(collection="work")
        assert len(docs) == 1
        assert docs[0].collection == "work"
        assert docs[0].title == "Test"

    def test_list_collections(self, sample_store: VstashStore) -> None:
        dim = sample_store.embedding_dim
        sample_store.add_document(
            path="/a.md",
            title="A",
            chunks=["a"],
            embeddings=[[0.1] * dim],
            collection="alpha",
        )
        sample_store.add_document(
            path="/b.md",
            title="B",
            chunks=["b"],
            embeddings=[[0.2] * dim],
            collection="beta",
        )
        sample_store.add_document(
            path="/c.md",
            title="C",
            chunks=["c"],
            embeddings=[[0.3] * dim],
            collection="alpha",
        )
        cols = sample_store.list_collections()
        assert set(cols) == {"alpha", "beta"}

    def test_list_filtered_by_collection(self, sample_store: VstashStore) -> None:
        dim = sample_store.embedding_dim
        sample_store.add_document(
            path="/a.md",
            title="A",
            chunks=["a"],
            embeddings=[[0.1] * dim],
            collection="proj1",
        )
        sample_store.add_document(
            path="/b.md",
            title="B",
            chunks=["b"],
            embeddings=[[0.2] * dim],
            collection="proj2",
        )
        docs_p1 = sample_store.list_documents(collection="proj1")
        assert len(docs_p1) == 1
        assert docs_p1[0].title == "A"

        docs_all = sample_store.list_documents()
        assert len(docs_all) == 2

    def test_search_scoped_by_collection(self, sample_store: VstashStore) -> None:
        dim = sample_store.embedding_dim
        sample_store.add_document(
            path="/work/a.md",
            title="Work Doc",
            chunks=["work content"],
            embeddings=[[0.1] * dim],
            collection="work",
        )
        sample_store.add_document(
            path="/personal/b.md",
            title="Personal Doc",
            chunks=["personal content"],
            embeddings=[[0.2] * dim],
            collection="personal",
        )
        query_vec = [0.1] * dim
        # Search only within "work"
        results = sample_store.search(query_vec, "content", collection="work")
        paths_in_results = {r.path for r in results}
        assert "/work/a.md" in paths_in_results or len(results) == 0
        # Ensure personal docs are NOT in work-scoped results
        assert "/personal/b.md" not in paths_in_results

    def test_find_document_scoped_by_collection(self, sample_store: VstashStore) -> None:
        dim = sample_store.embedding_dim
        sample_store.add_document(
            path="/docs/readme.md",
            title="Readme",
            chunks=["text"],
            embeddings=[[0.1] * dim],
            collection="docs",
        )
        sample_store.add_document(
            path="/notes/readme.md",
            title="Notes Readme",
            chunks=["notes"],
            embeddings=[[0.2] * dim],
            collection="notes",
        )
        match = sample_store.find_document("readme", collection="docs")
        assert match is not None
        assert "docs" in match

    def test_stats_includes_collection_count(self, sample_store: VstashStore) -> None:
        dim = sample_store.embedding_dim
        sample_store.add_document(
            path="/a.md",
            title="A",
            chunks=["a"],
            embeddings=[[0.1] * dim],
            collection="x",
        )
        sample_store.add_document(
            path="/b.md",
            title="B",
            chunks=["b"],
            embeddings=[[0.2] * dim],
            collection="y",
        )
        s = sample_store.stats()
        assert s.collections == 2

    def test_same_path_different_collections(self, sample_store: VstashStore) -> None:
        """Same file can exist in multiple collections (different doc_id hash)."""
        dim = sample_store.embedding_dim
        sample_store.add_document(
            path="/shared/file.md",
            title="Shared",
            chunks=["shared"],
            embeddings=[[0.1] * dim],
            collection="team-a",
        )
        sample_store.add_document(
            path="/shared/file.md",
            title="Shared",
            chunks=["shared"],
            embeddings=[[0.1] * dim],
            collection="team-b",
        )
        docs = sample_store.list_documents()
        assert len(docs) == 2
        cols = {d.collection for d in docs}
        assert cols == {"team-a", "team-b"}

    def test_default_collection(self, sample_store: VstashStore) -> None:
        """Documents without explicit collection go to 'default'."""
        dim = sample_store.embedding_dim
        sample_store.add_document(
            path="/x.md",
            title="X",
            chunks=["x"],
            embeddings=[[0.1] * dim],
        )
        docs = sample_store.list_documents()
        assert len(docs) == 1
        assert docs[0].collection == "default"
