"""Tests for DuckDBAdapter (guarded by duckdb availability)."""
import pytest

duckdb = pytest.importorskip("duckdb")

from kanoniv.adapters.duckdb import DuckDBAdapter
from kanoniv.source import Source


@pytest.fixture
def con():
    """In-memory DuckDB connection with a test table."""
    c = duckdb.connect()
    c.execute("""
        CREATE TABLE users (
            id INTEGER,
            name VARCHAR,
            score DOUBLE,
            active BOOLEAN,
            created DATE
        )
    """)
    c.execute("""
        INSERT INTO users VALUES
            (1, 'Alice', 9.5, true, '2024-01-01'),
            (2, 'Bob', 8.0, false, '2024-02-01'),
            (3, 'Charlie', 7.2, true, '2024-03-01')
    """)
    return c


class TestDuckDBAdapter:
    def test_schema_type_inference(self, con):
        adapter = DuckDBAdapter(con, "SELECT * FROM users")
        schema = adapter.schema()
        col_map = {c.name: c for c in schema.columns}
        assert col_map["id"].dtype == "number"
        assert col_map["name"].dtype == "string"
        assert col_map["score"].dtype == "number"
        assert col_map["active"].dtype == "boolean"
        assert col_map["created"].dtype == "date"

    def test_null_handling(self, con):
        con.execute("""
            CREATE TABLE with_nulls (name VARCHAR, score DOUBLE)
        """)
        con.execute("""
            INSERT INTO with_nulls VALUES ('Alice', 1.0), (NULL, NULL), ('Charlie', 3.0)
        """)
        adapter = DuckDBAdapter(con, "SELECT * FROM with_nulls")
        rows = list(adapter.iter_rows())
        assert rows[1]["name"] == ""
        assert rows[1]["score"] == ""

    def test_iter_rows_matches_query(self, con):
        adapter = DuckDBAdapter(con, "SELECT id, name FROM users WHERE id <= 2")
        rows = list(adapter.iter_rows())
        assert len(rows) == 2
        assert rows[0]["id"] == "1"
        assert rows[1]["name"] == "Bob"

    def test_row_count_returns_none(self, con):
        adapter = DuckDBAdapter(con, "SELECT * FROM users")
        assert adapter.row_count() is None

    def test_source_from_duckdb(self, con):
        src = Source.from_duckdb("test", con, "SELECT * FROM users", primary_key="id")
        entities = src.to_entities("customer")
        assert len(entities) == 3
        assert entities[0]["data"]["name"] == "Alice"

    def test_invalid_connection_raises(self):
        with pytest.raises(TypeError, match="DuckDBPyConnection"):
            DuckDBAdapter("not_a_connection", "SELECT 1")

    def test_query_with_aggregation(self, con):
        adapter = DuckDBAdapter(con, "SELECT name, COUNT(*) as cnt FROM users GROUP BY name")
        rows = list(adapter.iter_rows())
        assert len(rows) == 3
        assert all("cnt" in row for row in rows)

    def test_empty_result(self, con):
        adapter = DuckDBAdapter(con, "SELECT * FROM users WHERE id < 0")
        rows = list(adapter.iter_rows())
        assert rows == []

    def test_sample_values_in_schema(self, con):
        adapter = DuckDBAdapter(con, "SELECT * FROM users")
        schema = adapter.schema()
        name_col = next(c for c in schema.columns if c.name == "name")
        assert len(name_col.sample_values) == 3

    def test_table_name_as_query(self, con):
        adapter = DuckDBAdapter(con, "users")
        rows = list(adapter.iter_rows())
        assert len(rows) == 3
