"""Tests for ConcurrentDocumentStore Python binding (phase-30 / T30.5)."""

import os
import shutil
import tempfile
import time

import pytest

from aifastdb import ConcurrentDocumentStore


@pytest.fixture()
def tmp_dir():
    d = tempfile.mkdtemp(prefix="aifastdb_cds_")
    yield d
    shutil.rmtree(d, ignore_errors=True)


# ---------- Construction ----------

def test_create_default(tmp_dir):
    """Create a store with default settings."""
    path = os.path.join(tmp_dir, "default")
    store = ConcurrentDocumentStore(path)
    assert store.shard_count >= 1
    assert len(store) == 0
    assert store.is_empty()
    store.close()


def test_create_custom_shards(tmp_dir):
    """Create a store with explicit shard count."""
    path = os.path.join(tmp_dir, "shards")
    store = ConcurrentDocumentStore(path, shard_count=4, enable_wal=False)
    assert store.shard_count == 4
    store.close()


def test_create_high_throughput(tmp_dir):
    """Create a store in high_throughput mode."""
    path = os.path.join(tmp_dir, "ht")
    store = ConcurrentDocumentStore(path, mode="high_throughput", enable_wal=False)
    assert store.shard_count >= 1
    store.close()


def test_create_low_latency(tmp_dir):
    """Create a store in low_latency mode."""
    path = os.path.join(tmp_dir, "ll")
    store = ConcurrentDocumentStore(path, mode="low_latency", enable_wal=False)
    assert store.shard_count >= 1
    store.close()


# ---------- Write & Read ----------

def test_write_and_get(tmp_dir):
    """write() then get()."""
    path = os.path.join(tmp_dir, "wr")
    with ConcurrentDocumentStore(path, enable_wal=False) as store:
        store.write("d1", "Hello world", tags=["greeting"])
        # Flush to make sure data lands in the underlying store
        store.flush()
        time.sleep(0.15)

        doc = store.get("d1")
        assert doc is not None
        assert doc["id"] == "d1"
        assert doc["content"] == "Hello world"
        assert "greeting" in doc.get("tags", [])


def test_write_sync(tmp_dir):
    """write_sync() blocks until persisted."""
    path = os.path.join(tmp_dir, "ws")
    with ConcurrentDocumentStore(path, enable_wal=False) as store:
        store.write_sync("s1", "Sync content", doc_type="note")
        store.flush()
        time.sleep(0.15)

        doc = store.get("s1")
        assert doc is not None
        assert doc["content"] == "Sync content"


def test_write_batch(tmp_dir):
    """write_batch() with multiple docs."""
    path = os.path.join(tmp_dir, "wb")
    with ConcurrentDocumentStore(path, enable_wal=False) as store:
        count = store.write_batch([
            {"id": "b1", "content": "Batch one"},
            {"id": "b2", "content": "Batch two", "tags": ["batch"]},
            {"id": "b3", "content": "Batch three", "doc_type": "report"},
        ])
        assert count == 3
        store.flush()
        time.sleep(0.15)

        assert store.has("b1")
        assert store.has("b2")
        assert store.has("b3")
        assert len(store) == 3


def test_get_missing(tmp_dir):
    """get() returns None for non-existent doc."""
    path = os.path.join(tmp_dir, "miss")
    with ConcurrentDocumentStore(path, enable_wal=False) as store:
        assert store.get("nonexistent") is None


def test_has(tmp_dir):
    """has() returns True/False."""
    path = os.path.join(tmp_dir, "has")
    with ConcurrentDocumentStore(path, enable_wal=False) as store:
        assert not store.has("x")
        store.write_sync("x", "exists")
        store.flush()
        time.sleep(0.15)
        assert store.has("x")


def test_ids(tmp_dir):
    """ids() returns all document IDs."""
    path = os.path.join(tmp_dir, "ids")
    with ConcurrentDocumentStore(path, enable_wal=False) as store:
        store.write_sync("a", "A")
        store.write_sync("b", "B")
        store.flush()
        time.sleep(0.15)

        all_ids = store.ids()
        assert set(all_ids) == {"a", "b"}


# ---------- Management ----------

def test_metrics(tmp_dir):
    """metrics() returns a dict with expected keys."""
    path = os.path.join(tmp_dir, "met")
    with ConcurrentDocumentStore(path, enable_wal=False) as store:
        store.write_sync("m1", "Metrics test")
        store.flush()
        time.sleep(0.15)

        m = store.metrics()
        assert isinstance(m, dict)
        assert "total_requests" in m
        assert "successful_writes" in m
        assert "bytes_written" in m
        assert "success_rate" in m


def test_queue_fill_ratio(tmp_dir):
    """queue_fill_ratio() returns a float."""
    path = os.path.join(tmp_dir, "qfr")
    with ConcurrentDocumentStore(path, enable_wal=False) as store:
        ratio = store.queue_fill_ratio()
        assert isinstance(ratio, float)
        assert 0.0 <= ratio <= 1.0


def test_shard_count_property(tmp_dir):
    """shard_count is a property."""
    path = os.path.join(tmp_dir, "sc")
    store = ConcurrentDocumentStore(path, shard_count=2, enable_wal=False)
    assert store.shard_count == 2
    store.close()


def test_repr(tmp_dir):
    """__repr__ returns a readable string."""
    path = os.path.join(tmp_dir, "repr")
    with ConcurrentDocumentStore(path, enable_wal=False) as store:
        r = repr(store)
        assert "ConcurrentDocumentStore" in r
        assert "docs=" in r
        assert "shards=" in r


# ---------- Context Manager ----------

def test_context_manager(tmp_dir):
    """with-statement flushes and shuts down."""
    path = os.path.join(tmp_dir, "ctx")
    with ConcurrentDocumentStore(path, enable_wal=False) as store:
        store.write_sync("c1", "Context data")
        store.flush()
        time.sleep(0.15)
        assert len(store) == 1
    # After exiting context, the store should be cleanly shut down.
    # Re-opening to verify data persisted:
    store2 = ConcurrentDocumentStore(path, enable_wal=False)
    doc = store2.get("c1")
    assert doc is not None
    assert doc["content"] == "Context data"
    store2.close()


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
