"""Tests for schema definitions and validation."""

from __future__ import annotations

from orxaq.fabric.schema import ColumnDef, SchemaSpec, credit_schema
from orxaq.kernel import SensitivityLevel


class TestColumnDef:
    def test_basic_column(self):
        col = ColumnDef(name="score", dtype="float", bounds=(0.0, 100.0))
        assert col.name == "score"
        assert col.dtype == "float"
        assert col.bounds == (0.0, 100.0)

    def test_string_column_with_allowed_values(self):
        col = ColumnDef(name="status", dtype="str", allowed_values=("active", "closed"))
        assert col.allowed_values == ("active", "closed")

    def test_sensitivity_default(self):
        col = ColumnDef(name="x")
        assert col.sensitivity == SensitivityLevel.INTERNAL


class TestSchemaSpec:
    def setup_method(self):
        self.schema = SchemaSpec(
            name="test",
            columns=(
                ColumnDef("id", "int", nullable=False),
                ColumnDef("value", "float", bounds=(0.0, 100.0)),
                ColumnDef("name", "str"),
            ),
        )

    def test_valid_data_passes(self):
        data = [{"id": 1, "value": 50.0, "name": "Alice"}]
        result = self.schema.validate(data)
        assert result.valid is True
        assert result.error_count == 0

    def test_null_in_non_nullable_fails(self):
        data = [{"id": None, "value": 50.0, "name": "Alice"}]
        result = self.schema.validate(data)
        assert result.valid is False
        assert result.error_count == 1
        assert "null" in result.errors[0].message

    def test_type_mismatch_fails(self):
        data = [{"id": 1, "value": "not_a_number", "name": "Alice"}]
        result = self.schema.validate(data)
        assert result.valid is False
        assert "expected float" in result.errors[0].message

    def test_out_of_bounds_fails(self):
        data = [{"id": 1, "value": 150.0, "name": "Alice"}]
        result = self.schema.validate(data)
        assert result.valid is False
        assert "above maximum" in result.errors[0].message

    def test_below_bounds_fails(self):
        data = [{"id": 1, "value": -5.0, "name": "Alice"}]
        result = self.schema.validate(data)
        assert result.valid is False
        assert "below minimum" in result.errors[0].message

    def test_nullable_column_allows_none(self):
        data = [{"id": 1, "value": None, "name": None}]
        result = self.schema.validate(data)
        assert result.valid is True

    def test_multiple_rows(self):
        data = [
            {"id": 1, "value": 50.0, "name": "A"},
            {"id": None, "value": 200.0, "name": "B"},
        ]
        result = self.schema.validate(data)
        assert result.valid is False
        assert result.error_count == 2  # null id + out of bounds value
        assert result.total_rows == 2

    def test_allowed_values(self):
        schema = SchemaSpec(
            name="enum_test",
            columns=(ColumnDef("status", "str", allowed_values=("active", "closed")),),
        )
        good = [{"status": "active"}]
        bad = [{"status": "unknown"}]
        assert schema.validate(good).valid is True
        assert schema.validate(bad).valid is False

    def test_empty_data(self):
        result = self.schema.validate([])
        assert result.valid is True
        assert result.total_rows == 0


class TestCreditSchema:
    def test_has_19_columns(self):
        schema = credit_schema()
        assert len(schema.columns) == 19

    def test_name(self):
        schema = credit_schema()
        assert schema.name == "credit_risk"

    def test_column_names_match_ontology(self):
        from orxaq.credit.ontology import CREDIT_VARIABLES

        schema = credit_schema()
        schema_cols = {c.name for c in schema.columns}
        assert schema_cols == set(CREDIT_VARIABLES.keys())


# ── Targeted coverage (lines 120-122: bool type check) ──────


class TestBoolTypeValidation:
    """Cover lines 120-122: bool type validation in _validate_type."""

    def test_bool_column_valid(self):
        schema = SchemaSpec(
            name="bool_test",
            columns=(ColumnDef("active", "bool"),),
        )
        data = [{"active": True}, {"active": False}]
        result = schema.validate(data)
        assert result.valid is True

    def test_bool_column_wrong_type(self):
        schema = SchemaSpec(
            name="bool_test",
            columns=(ColumnDef("active", "bool"),),
        )
        data = [{"active": "yes"}]
        result = schema.validate(data)
        assert result.valid is False

    def test_unknown_dtype_always_valid(self):
        """Cover line 122: unknown dtype passes _check_type."""
        assert SchemaSpec._check_type("anything", "datetime") is True
