"""Unit tests for DocumentStore Python bindings (T27.1 / T27.8).

Tests:
- CRUD: put, get, has, delete
- Queries: find_by_tag, find_by_type, find_by_time_range, find_by_min_importance
- Lifecycle: context manager, flush, compact, __len__, is_empty, ids, all_tags, all
"""

import os
import sys
import tempfile
import time

# Ensure the Python package is importable
pkg_dir = os.path.join(os.path.dirname(__file__), "..", "python")
if pkg_dir not in sys.path:
    sys.path.insert(0, pkg_dir)

from aifastdb import DocumentStore


def get_temp_path(name="test_docs.jsonl"):
    """Return a temporary file path for a document store."""
    return os.path.join(tempfile.mkdtemp(prefix="aifastdb_test_"), name)


def test_basic_crud():
    """Test put, get, has, delete."""
    path = get_temp_path()
    try:
        with DocumentStore(path) as store:
            # put
            store.put("d1", "Hello World", doc_type="text", tags=["greeting", "en"])
            store.put("d2", "def foo(): pass", doc_type="code", tags=["python"])

            # has
            assert store.has("d1"), "d1 should exist"
            assert store.has("d2"), "d2 should exist"
            assert not store.has("d999"), "d999 should not exist"

            # get
            doc = store.get("d1")
            assert doc is not None, "d1 should be retrievable"
            assert doc["id"] == "d1"
            assert doc["content"] == "Hello World"
            assert "greeting" in doc["tags"]
            assert "en" in doc["tags"]

            # get non-existent
            assert store.get("d999") is None

            # delete
            deleted = store.delete("d1")
            assert deleted is not None, "delete should return the deleted doc"
            assert deleted["id"] == "d1"
            assert not store.has("d1"), "d1 should be deleted"

            # delete non-existent
            assert store.delete("d999") is None

        print("✓ test_basic_crud passed")
    finally:
        _cleanup(path)


def test_put_with_metadata():
    """Test put with metadata and importance."""
    path = get_temp_path()
    try:
        with DocumentStore(path) as store:
            store.put(
                "m1",
                "Important note",
                doc_type="document",
                tags=["important"],
                metadata={"author": "alice", "priority": 1},
                importance=0.9,
            )

            doc = store.get("m1")
            assert doc is not None
            assert doc["importance"] == 0.9
            assert doc["metadata"]["author"] == "alice"
            assert doc["metadata"]["priority"] == 1

        print("✓ test_put_with_metadata passed")
    finally:
        _cleanup(path)


def test_find_by_tag():
    """Test find_by_tag."""
    path = get_temp_path()
    try:
        with DocumentStore(path) as store:
            store.put("t1", "A", tags=["alpha", "beta"])
            store.put("t2", "B", tags=["beta", "gamma"])
            store.put("t3", "C", tags=["gamma"])

            # Find by 'beta'
            results = store.find_by_tag("beta")
            ids = {r["id"] for r in results}
            assert ids == {"t1", "t2"}, f"Expected t1,t2; got {ids}"

            # Find by 'gamma'
            results = store.find_by_tag("gamma")
            ids = {r["id"] for r in results}
            assert ids == {"t2", "t3"}, f"Expected t2,t3; got {ids}"

            # Find by non-existent tag
            results = store.find_by_tag("nonexistent")
            assert len(results) == 0

        print("✓ test_find_by_tag passed")
    finally:
        _cleanup(path)


def test_find_by_type():
    """Test find_by_type."""
    path = get_temp_path()
    try:
        with DocumentStore(path) as store:
            store.put("ft1", "text content", doc_type="text")
            store.put("ft2", "code content", doc_type="code")
            store.put("ft3", "more text", doc_type="text")

            text_docs = store.find_by_type("text")
            ids = {r["id"] for r in text_docs}
            assert ids == {"ft1", "ft3"}, f"Expected ft1,ft3; got {ids}"

            code_docs = store.find_by_type("code")
            ids = {r["id"] for r in code_docs}
            assert ids == {"ft2"}, f"Expected ft2; got {ids}"

        print("✓ test_find_by_type passed")
    finally:
        _cleanup(path)


def test_find_by_min_importance():
    """Test find_by_min_importance."""
    path = get_temp_path()
    try:
        with DocumentStore(path) as store:
            store.put("i1", "Low", importance=0.1)
            store.put("i2", "Medium", importance=0.5)
            store.put("i3", "High", importance=0.9)

            results = store.find_by_min_importance(0.5)
            ids = {r["id"] for r in results}
            assert "i2" in ids, f"i2 (0.5) should be included; got {ids}"
            assert "i3" in ids, f"i3 (0.9) should be included; got {ids}"
            assert "i1" not in ids, f"i1 (0.1) should not be included"

        print("✓ test_find_by_min_importance passed")
    finally:
        _cleanup(path)


def test_len_and_ids():
    """Test __len__, is_empty, ids, all_tags."""
    path = get_temp_path()
    try:
        with DocumentStore(path) as store:
            assert len(store) == 0
            assert store.is_empty()

            store.put("l1", "A", tags=["x"])
            store.put("l2", "B", tags=["y", "z"])

            assert len(store) == 2
            assert not store.is_empty()

            ids = set(store.ids())
            assert ids == {"l1", "l2"}

            tags = set(store.all_tags())
            assert tags == {"x", "y", "z"}, f"Expected x,y,z; got {tags}"

        print("✓ test_len_and_ids passed")
    finally:
        _cleanup(path)


def test_all():
    """Test all() returns all documents."""
    path = get_temp_path()
    try:
        with DocumentStore(path) as store:
            store.put("a1", "Alpha")
            store.put("a2", "Beta")
            store.put("a3", "Gamma")

            docs = store.all()
            assert len(docs) == 3
            ids = {d["id"] for d in docs}
            assert ids == {"a1", "a2", "a3"}

        print("✓ test_all passed")
    finally:
        _cleanup(path)


def test_context_manager_flush():
    """Test that context manager flushes on exit and data persists."""
    path = get_temp_path()
    try:
        # Write data (disable file lock so we can re-open in the same process)
        with DocumentStore(path, enable_file_lock=False) as store:
            store.put("p1", "Persist me", tags=["persist"])
            store.flush()

        # Small delay for background writer to complete disk write
        time.sleep(0.5)

        # Re-open and verify persistence
        with DocumentStore(path, enable_file_lock=False) as store:
            doc = store.get("p1")
            assert doc is not None, "p1 should persist after re-open"
            assert doc["content"] == "Persist me"

        print("✓ test_context_manager_flush passed")
    finally:
        _cleanup(path)


def test_repr():
    """Test __repr__."""
    path = get_temp_path()
    try:
        with DocumentStore(path) as store:
            store.put("r1", "repr test")
            r = repr(store)
            assert "DocumentStore" in r
            assert "len=1" in r

        print("✓ test_repr passed")
    finally:
        _cleanup(path)


def _cleanup(path):
    """Remove test files."""
    import shutil
    parent = os.path.dirname(path)
    if os.path.exists(parent):
        shutil.rmtree(parent, ignore_errors=True)


if __name__ == "__main__":
    test_basic_crud()
    test_put_with_metadata()
    test_find_by_tag()
    test_find_by_type()
    test_find_by_min_importance()
    test_len_and_ids()
    test_all()
    test_context_manager_flush()
    test_repr()
    print("\n✅ All DocumentStore tests passed!")
