"""Tests for rule-to-validator adapters."""

from __future__ import annotations

import pytest

from truthound.profiler.generators.base import GeneratedRule, RuleCategory, RuleConfidence
from truthound.profiler.integration.adapters import (
    create_validator_from_rule,
    DefaultRuleAdapter,
    DynamicImportAdapter,
    ValidatorRegistry,
)


class TestParameterTransformation:
    """Test parameter transformation in adapters."""

    def test_column_type_expected_type_to_expected_types(self):
        """Test that expected_type is converted to expected_types dict."""
        rule = GeneratedRule(
            name="column_type_age",
            validator_class="ColumnTypeValidator",
            category=RuleCategory.SCHEMA,
            columns=("age",),
            parameters={"expected_type": "int64"},
        )

        validator = create_validator_from_rule(rule)

        # Validator should have expected_types as a dict
        assert hasattr(validator, "expected_types")
        assert isinstance(validator.expected_types, dict)
        assert "age" in validator.expected_types
        assert validator.expected_types["age"] == "int64"

    def test_column_type_multiple_columns(self):
        """Test expected_type with multiple columns."""
        rule = GeneratedRule(
            name="column_type_numeric",
            validator_class="ColumnTypeValidator",
            category=RuleCategory.SCHEMA,
            columns=("id", "count", "total"),
            parameters={"expected_type": "int64"},
        )

        validator = create_validator_from_rule(rule)

        assert hasattr(validator, "expected_types")
        assert validator.expected_types == {
            "id": "int64",
            "count": "int64",
            "total": "int64",
        }

    def test_column_type_no_columns_fallback(self):
        """Test expected_type with no columns specified."""
        rule = GeneratedRule(
            name="column_type_default",
            validator_class="ColumnTypeValidator",
            category=RuleCategory.SCHEMA,
            columns=(),
            parameters={"expected_type": "string"},
        )

        validator = create_validator_from_rule(rule)

        assert hasattr(validator, "expected_types")
        assert "__default__" in validator.expected_types

    def test_non_transformed_parameters_passed_through(self):
        """Test that non-transformed parameters are passed through unchanged."""
        rule = GeneratedRule(
            name="not_null_test",
            validator_class="NotNullValidator",
            category=RuleCategory.COMPLETENESS,
            columns=("id",),
            parameters={},
        )

        validator = create_validator_from_rule(rule)

        # Validators store columns in config
        assert hasattr(validator, "config")
        assert validator.config.columns is not None
        assert "id" in validator.config.columns


class TestDefaultRuleAdapter:
    """Test DefaultRuleAdapter class."""

    def test_supports_any_rule_with_validator_class(self):
        """Test that adapter supports any rule with validator_class."""
        adapter = DefaultRuleAdapter()

        rule_with_class = GeneratedRule(
            name="test",
            validator_class="SomeValidator",
            category=RuleCategory.SCHEMA,
        )
        rule_without_class = GeneratedRule(
            name="test",
            validator_class="",
            category=RuleCategory.SCHEMA,
        )

        assert adapter.supports(rule_with_class) is True
        assert adapter.supports(rule_without_class) is False

    def test_priority_is_zero(self):
        """Test that default adapter has lowest priority."""
        adapter = DefaultRuleAdapter()
        assert adapter.priority == 0

    def test_transform_parameters_column_type(self):
        """Test parameter transformation for column type validator."""
        adapter = DefaultRuleAdapter()
        rule = GeneratedRule(
            name="test",
            validator_class="ColumnTypeValidator",
            category=RuleCategory.SCHEMA,
            columns=("col1",),
            parameters={"expected_type": "float64"},
        )

        params = adapter._transform_parameters(
            "column_type",
            {"expected_type": "float64"},
            rule,
        )

        assert "expected_types" in params
        assert params["expected_types"] == {"col1": "float64"}
        assert "expected_type" not in params


class TestDynamicImportAdapter:
    """Test DynamicImportAdapter class."""

    def test_priority_is_higher_than_default(self):
        """Test that dynamic adapter has higher priority."""
        dynamic = DynamicImportAdapter()
        default = DefaultRuleAdapter()
        assert dynamic.priority > default.priority

    def test_applies_parameter_transformations(self):
        """Test that dynamic adapter also applies transformations."""
        rule = GeneratedRule(
            name="column_type_test",
            validator_class="ColumnTypeValidator",
            category=RuleCategory.SCHEMA,
            columns=("name",),
            parameters={"expected_type": "string"},
        )

        adapter = DynamicImportAdapter()
        validator = adapter.convert(rule)

        assert hasattr(validator, "expected_types")
        assert validator.expected_types == {"name": "string"}


class TestValidatorRegistry:
    """Test ValidatorRegistry class."""

    def test_singleton_instance(self):
        """Test that get_instance returns same instance."""
        instance1 = ValidatorRegistry.get_instance()
        instance2 = ValidatorRegistry.get_instance()
        assert instance1 is instance2

    def test_create_validator_with_transformation(self):
        """Test creating validator through registry with transformation."""
        registry = ValidatorRegistry()
        rule = GeneratedRule(
            name="column_type_email",
            validator_class="ColumnTypeValidator",
            category=RuleCategory.SCHEMA,
            columns=("email",),
            parameters={"expected_type": "string"},
        )

        validator = registry.create_validator(rule)

        assert hasattr(validator, "expected_types")
        assert validator.expected_types == {"email": "string"}

    def test_custom_factory_bypasses_adapters(self):
        """Test that custom factory takes precedence."""
        registry = ValidatorRegistry()

        custom_called = []

        def custom_factory(rule):
            custom_called.append(rule.name)
            # Return a mock validator
            from truthound.validators import NotNullValidator
            return NotNullValidator(columns=["test"])

        registry.register_factory("CustomValidator", custom_factory)

        rule = GeneratedRule(
            name="custom_test",
            validator_class="CustomValidator",
            category=RuleCategory.SCHEMA,
        )

        validator = registry.create_validator(rule)

        assert "custom_test" in custom_called
        # Validators store columns in config
        assert hasattr(validator, "config")
        assert validator.config.columns is not None


class TestCreateValidatorFromRule:
    """Test the create_validator_from_rule convenience function."""

    def test_creates_column_type_validator(self):
        """Test creating ColumnTypeValidator from rule."""
        rule = GeneratedRule(
            name="test_type",
            validator_class="ColumnTypeValidator",
            category=RuleCategory.SCHEMA,
            columns=("price",),
            parameters={"expected_type": "float64"},
        )

        validator = create_validator_from_rule(rule)

        assert validator.__class__.__name__ == "ColumnTypeValidator"
        assert validator.expected_types == {"price": "float64"}

    def test_creates_not_null_validator(self):
        """Test creating NotNullValidator from rule."""
        rule = GeneratedRule(
            name="test_not_null",
            validator_class="NotNullValidator",
            category=RuleCategory.COMPLETENESS,
            columns=("id", "name"),
        )

        validator = create_validator_from_rule(rule)

        assert validator.__class__.__name__ == "NotNullValidator"
        # Validators store columns in config
        assert set(validator.config.columns) == {"id", "name"}

    def test_creates_validator_with_mostly(self):
        """Test creating validator with mostly parameter."""
        rule = GeneratedRule(
            name="test_mostly",
            validator_class="NotNullValidator",
            category=RuleCategory.COMPLETENESS,
            columns=("optional_field",),
            mostly=0.95,
        )

        validator = create_validator_from_rule(rule)

        # Mostly is stored in config
        assert hasattr(validator, "config")
        assert validator.config.mostly == 0.95

    def test_raises_for_unknown_validator(self):
        """Test that unknown validator raises ValueError."""
        rule = GeneratedRule(
            name="test_unknown",
            validator_class="NonExistentValidator",
            category=RuleCategory.SCHEMA,
        )

        with pytest.raises(ValueError, match="(Cannot create|No adapter can create)"):
            create_validator_from_rule(rule)


class TestIntegrationWithSchemaRules:
    """Integration tests with SchemaRuleGenerator output."""

    def test_schema_rule_generator_column_type_rule(self):
        """Test that SchemaRuleGenerator column type rules convert correctly."""
        import polars as pl
        from truthound.profiler import profile_dataframe
        from truthound.profiler.generators.schema_rules import SchemaRuleGenerator
        from truthound.profiler.base import Strictness

        # Create test data
        df = pl.DataFrame({
            "id": [1, 2, 3],
            "name": ["a", "b", "c"],
            "price": [1.0, 2.0, 3.0],
        })

        # Profile the data
        profile = profile_dataframe(df)

        # Generate rules
        generator = SchemaRuleGenerator()
        rules = generator.generate(profile, Strictness.MEDIUM)

        # Find column type rules
        type_rules = [r for r in rules if "column_type" in r.name]

        # Verify each rule can be converted to a validator
        for rule in type_rules:
            validator = create_validator_from_rule(rule)
            assert validator is not None
            assert hasattr(validator, "expected_types")

            # Verify the validator works
            issues = validator.validate(df.lazy())
            # Should have no issues for correct types
            assert len(issues) == 0, f"Unexpected issues: {issues}"

    def test_full_suite_execute_integration(self):
        """Test that generated rules work with Suite.execute()."""
        import polars as pl
        from truthound.profiler import profile_dataframe
        from truthound.profiler.generators.schema_rules import SchemaRuleGenerator
        from truthound.profiler.base import Strictness

        # Create test data
        df = pl.DataFrame({
            "user_id": [1, 2, 3, 4, 5],
            "email": ["a@b.com", "c@d.com", "e@f.com", "g@h.com", "i@j.com"],
            "active": [True, False, True, True, False],
        })

        # Profile the data
        profile = profile_dataframe(df)

        # Generate rules
        generator = SchemaRuleGenerator()
        rules = generator.generate(profile, Strictness.MEDIUM)

        # Convert rules to validators
        validators = []
        for rule in rules:
            try:
                validator = create_validator_from_rule(rule)
                validators.append(validator)
            except ValueError:
                # Some validators may not be available
                pass

        # Verify we have some validators
        assert len(validators) > 0

        # Run all validators
        all_issues = []
        for validator in validators:
            issues = validator.validate(df.lazy())
            all_issues.extend(issues)

        # Should have no issues for valid data
        assert len(all_issues) == 0, f"Unexpected issues: {all_issues}"
