"""Tests para cache_to_sqlite."""

import os
import tempfile
import threading
import time

import pytest

from .cache_to_sqlite import cache_to_sqlite


@pytest.fixture
def store(tmp_path):
    db = str(tmp_path / "test.db")
    return cache_to_sqlite(db)


@pytest.fixture
def store2(tmp_path):
    """Segundo namespace en la misma BD."""
    db = str(tmp_path / "test.db")
    return cache_to_sqlite(db, namespace="other")


@pytest.fixture
def store_and_other(tmp_path):
    db = str(tmp_path / "test.db")
    s1 = cache_to_sqlite(db, namespace="ns1")
    s2 = cache_to_sqlite(db, namespace="ns2")
    return s1, s2


def test_set_y_get_basico(store):
    store.set("foo", {"x": 1})
    assert store.get("foo") == {"x": 1}


def test_ttl_expirado_retorna_none(store):
    store.set("expiring", "hello", ttl=0.05)
    time.sleep(0.1)
    assert store.get("expiring") is None


def test_ttl_cero_nunca_expira(store):
    store.set("forever", 42, ttl=0)
    time.sleep(0.05)
    assert store.get("forever") == 42


def test_get_or_set_factory_solo_se_llama_en_miss(store):
    calls = []

    def factory():
        calls.append(1)
        return "computed"

    result1 = store.get_or_set("key", factory, ttl=10)
    result2 = store.get_or_set("key", factory, ttl=10)
    assert result1 == "computed"
    assert result2 == "computed"
    assert len(calls) == 1


def test_namespaces_independientes(store_and_other):
    s1, s2 = store_and_other
    s1.set("k", "from_ns1")
    assert s2.get("k") is None
    s2.set("k", "from_ns2")
    assert s1.get("k") == "from_ns1"
    assert s2.get("k") == "from_ns2"


def test_clear_elimina_solo_el_namespace(store_and_other):
    s1, s2 = store_and_other
    s1.set("a", 1)
    s2.set("b", 2)
    removed = s1.clear()
    assert removed == 1
    assert s1.get("a") is None
    assert s2.get("b") == 2


def test_stats_contadores_correctos(store):
    store.set("x", 10)
    store.get("x")   # hit
    store.get("x")   # hit
    store.get("z")   # miss
    s = store.stats()
    assert s["hits"] == 2
    assert s["misses"] == 1
    assert s["size"] == 1


def test_concurrencia(tmp_path):
    db = str(tmp_path / "concurrent.db")
    s = cache_to_sqlite(db, "parallel")
    errors = []

    def worker(i):
        try:
            s.set(f"key_{i}", i)
            val = s.get(f"key_{i}")
            assert val == i
        except Exception as e:
            errors.append(e)

    threads = [threading.Thread(target=worker, args=(i,)) for i in range(20)]
    for t in threads:
        t.start()
    for t in threads:
        t.join()

    assert errors == [], f"Errors in threads: {errors}"
