"""Unit tests for validation/__init__.py module.

Tests validation module initialization functions including get_required_attributes,
get_recommended_attributes, and validate_resource_attributes.
"""

import pytest

from mca_sdk.validation import (
    get_required_attributes,
    get_recommended_attributes,
    validate_resource_attributes,
    REQUIRED_ATTRIBUTES,
    RECOMMENDED_ATTRIBUTES,
)
from mca_sdk.utils.exceptions import ValidationError


class TestGetRequiredAttributes:
    """Test get_required_attributes function."""

    def test_get_required_attributes_internal(self):
        """Test getting required attributes for internal model type."""
        result = get_required_attributes("internal")

        assert isinstance(result, list)
        assert "service.name" in result
        assert "model.id" in result
        assert "model.version" in result
        assert "team.name" in result

    def test_get_required_attributes_generative(self):
        """Test getting required attributes for generative model type."""
        result = get_required_attributes("generative")

        assert isinstance(result, list)
        assert "service.name" in result
        assert "llm.provider" in result
        assert "llm.model" in result
        assert "team.name" in result

    def test_get_required_attributes_vendor(self):
        """Test getting required attributes for vendor model type."""
        result = get_required_attributes("vendor")

        assert isinstance(result, list)
        assert "service.name" in result
        assert "vendor.name" in result
        assert "team.name" in result

    def test_get_required_attributes_regression_maps_to_internal(self):
        """Test that regression model type maps to internal."""
        result = get_required_attributes("regression")

        # Should return same as internal
        internal_attrs = get_required_attributes("internal")
        assert result == internal_attrs

    def test_get_required_attributes_time_series_maps_to_internal(self):
        """Test that time-series model type maps to internal."""
        result = get_required_attributes("time-series")

        # Should return same as internal
        internal_attrs = get_required_attributes("internal")
        assert result == internal_attrs

    def test_get_required_attributes_classification_maps_to_internal(self):
        """Test that classification model type maps to internal."""
        result = get_required_attributes("classification")

        # Should return same as internal
        internal_attrs = get_required_attributes("internal")
        assert result == internal_attrs

    def test_get_required_attributes_agentic_maps_to_generative(self):
        """Test that agentic model type maps to generative."""
        result = get_required_attributes("agentic")

        # Should return same as generative
        generative_attrs = get_required_attributes("generative")
        assert result == generative_attrs

    def test_get_required_attributes_invalid_type_raises_error(self):
        """Test that invalid model type raises ValidationError."""
        with pytest.raises(ValidationError, match="Invalid model_type"):
            get_required_attributes("invalid_type")

    def test_get_required_attributes_returns_copy(self):
        """Test that function returns a copy, not reference."""
        result1 = get_required_attributes("internal")
        result2 = get_required_attributes("internal")

        # Should be equal but not same object
        assert result1 == result2
        assert result1 is not result2

        # Modifying one shouldn't affect the other
        result1.append("test_attribute")
        assert "test_attribute" not in result2


class TestGetRecommendedAttributes:
    """Test get_recommended_attributes function."""

    def test_get_recommended_attributes_returns_list(self):
        """Test that function returns a list."""
        result = get_recommended_attributes()

        assert isinstance(result, list)

    def test_get_recommended_attributes_includes_expected_attrs(self):
        """Test that recommended attributes include expected values."""
        result = get_recommended_attributes()

        # Should include service.version and deployment.environment
        assert "service.version" in result
        assert "deployment.environment" in result

    def test_get_recommended_attributes_returns_copy(self):
        """Test that function returns a copy, not reference."""
        result1 = get_recommended_attributes()
        result2 = get_recommended_attributes()

        # Should be equal but not same object
        assert result1 == result2
        assert result1 is not result2

        # Modifying one shouldn't affect the other
        result1.append("test_attribute")
        assert "test_attribute" not in result2


class TestValidateResourceAttributes:
    """Test validate_resource_attributes function."""

    def test_validate_valid_internal_attributes(self):
        """Test validation passes for valid internal model attributes."""
        attributes = {
            "service.name": "test-service",
            "model.id": "mdl-001",
            "model.version": "1.0.0",
            "team.name": "test-team",
        }

        # Should not raise
        validate_resource_attributes(attributes, model_category="internal", model_type="regression")

    def test_validate_valid_generative_attributes(self):
        """Test validation passes for valid generative model attributes."""
        attributes = {
            "service.name": "llm-service",
            "llm.provider": "openai",
            "llm.model": "gpt-4",
            "team.name": "ai-team",
        }

        # Should not raise
        validate_resource_attributes(attributes, model_category="internal", model_type="generative")

    def test_validate_valid_vendor_attributes(self):
        """Test validation passes for valid vendor model attributes."""
        attributes = {
            "service.name": "vendor-service",
            "vendor.name": "acme-corp",
            "team.name": "integration-team",
        }

        # Should not raise
        validate_resource_attributes(
            attributes, model_category="vendor", model_type="classification"
        )

    def test_validate_missing_required_attribute_strict_mode(self):
        """Test that missing required attribute raises error in strict mode."""
        attributes = {
            "service.name": "test-service",
            "model.id": "mdl-001",
            # Missing model.version and team.name
        }

        with pytest.raises(ValidationError, match="Missing required resource attributes"):
            validate_resource_attributes(
                attributes,
                model_category="internal",
                model_type="regression",
                strict=True,
            )

    def test_validate_missing_required_attribute_non_strict_mode(self):
        """Test that missing required attribute doesn't raise in non-strict mode."""
        attributes = {
            "service.name": "test-service",
            "model.id": "mdl-001",
            # Missing model.version and team.name
        }

        # Should not raise in non-strict mode
        validate_resource_attributes(
            attributes, model_category="internal", model_type="regression", strict=False
        )

    def test_validate_none_value_treated_as_missing(self):
        """Test that None value is treated as missing."""
        attributes = {
            "service.name": "test-service",
            "model.id": None,  # None should be treated as missing
            "model.version": "1.0.0",
            "team.name": "test-team",
        }

        with pytest.raises(ValidationError, match="Missing required resource attributes"):
            validate_resource_attributes(
                attributes, model_category="internal", model_type="regression"
            )

    def test_validate_empty_string_treated_as_missing(self):
        """Test that empty string is treated as missing."""
        attributes = {
            "service.name": "test-service",
            "model.id": "",  # Empty string should be treated as missing
            "model.version": "1.0.0",
            "team.name": "test-team",
        }

        with pytest.raises(ValidationError, match="Missing required resource attributes"):
            validate_resource_attributes(
                attributes, model_category="internal", model_type="regression"
            )

    def test_validate_whitespace_string_treated_as_missing(self):
        """Test that whitespace-only string is treated as missing."""
        attributes = {
            "service.name": "test-service",
            "model.id": "   ",  # Whitespace-only should be treated as missing
            "model.version": "1.0.0",
            "team.name": "test-team",
        }

        with pytest.raises(ValidationError, match="Missing required resource attributes"):
            validate_resource_attributes(
                attributes, model_category="internal", model_type="regression"
            )

    def test_validate_value_exceeds_max_length(self):
        """Test that attribute value exceeding max length raises error."""
        attributes = {
            "service.name": "x" * 1025,  # Exceeds 1024 character limit
            "model.id": "mdl-001",
            "model.version": "1.0.0",
            "team.name": "test-team",
        }

        with pytest.raises(ValidationError, match="exceeds maximum length"):
            validate_resource_attributes(
                attributes, model_category="internal", model_type="regression"
            )

    def test_validate_value_at_exact_max_length_passes(self):
        """Test that attribute value at exactly max length passes."""
        attributes = {
            "service.name": "x" * 1024,  # Exactly at limit
            "model.id": "mdl-001",
            "model.version": "1.0.0",
            "team.name": "test-team",
        }

        # Should not raise
        validate_resource_attributes(attributes, model_category="internal", model_type="regression")

    def test_validate_error_message_includes_helpful_config_example(self):
        """Test that error message includes helpful configuration example."""
        attributes = {
            "service.name": "test-service",
            # Missing other required attributes
        }

        try:
            validate_resource_attributes(
                attributes, model_category="internal", model_type="regression"
            )
        except ValidationError as e:
            error_msg = str(e)
            # Should include configuration example
            assert "MCAClient(" in error_msg
            assert "model_id=" in error_msg or "model.id=" in error_msg
            assert "Metric prefix" in error_msg

    def test_validate_invalid_taxonomy_raises_error(self):
        """Test that invalid taxonomy combination raises ValidationError."""
        attributes = {
            "service.name": "test-service",
            "model.id": "mdl-001",
            "model.version": "1.0.0",
            "team.name": "test-team",
        }

        with pytest.raises(ValidationError, match="Invalid taxonomy"):
            validate_resource_attributes(attributes, model_category="invalid", model_type="invalid")

    def test_validate_with_extra_attributes_allowed(self):
        """Test that extra attributes beyond required are allowed."""
        attributes = {
            "service.name": "test-service",
            "model.id": "mdl-001",
            "model.version": "1.0.0",
            "team.name": "test-team",
            "extra.attribute": "extra-value",  # Extra attribute
            "another.one": "another-value",
        }

        # Should not raise - extra attributes are allowed
        validate_resource_attributes(attributes, model_category="internal", model_type="regression")


class TestModuleConstants:
    """Test module-level constants."""

    def test_required_attributes_contains_expected_keys(self):
        """Test REQUIRED_ATTRIBUTES contains expected model types."""
        assert "internal" in REQUIRED_ATTRIBUTES
        assert "generative" in REQUIRED_ATTRIBUTES
        assert "vendor" in REQUIRED_ATTRIBUTES

    def test_required_attributes_values_are_lists(self):
        """Test REQUIRED_ATTRIBUTES values are lists."""
        for model_type, attrs in REQUIRED_ATTRIBUTES.items():
            assert isinstance(attrs, list)
            assert len(attrs) > 0

    def test_recommended_attributes_is_list(self):
        """Test RECOMMENDED_ATTRIBUTES is a list."""
        assert isinstance(RECOMMENDED_ATTRIBUTES, list)
