"""Integration tests for Story 3.14: Schema Validation.

These tests verify the acceptance criteria for schema validation
with MCAClient integration.
"""

import pytest
from mca_sdk import MCAClient, MCAConfig, ValidationError


class TestAC1ValidMetricNamesPass:
    """AC #1: Valid metric name passes validation and is exported."""

    def test_model_predictions_total_passes(self):
        """Given I record model.predictions_total, then it passes validation."""
        config = MCAConfig(
            service_name="test-service",
            model_id="test-model",
            team_name="test-team",
            model_category="internal",
            model_type="regression",
            strict_validation=True,
        )
        client = MCAClient(config=config)

        # Should not raise - valid name and structure
        client.record_metric("emms_model_predictions_total", 1.0)
        client.shutdown()


class TestAC5ProtobufSchemaValidation:
    """AC #5: Protobuf schema validation for metric structure."""

    def test_invalid_value_type_rejected(self):
        """Given invalid value type, then ValidationError raised."""
        from mca_sdk.validation.schema import MetricNamingConvention

        validator = MetricNamingConvention(
            model_category="internal", model_type="regression", strict=True
        )

        metric_data = {
            "name": "model.predictions_total",
            "value": "not_a_number",  # Invalid: string instead of number
            "attributes": {},
        }

        with pytest.raises(Exception) as exc_info:
            validator.validate("model.predictions_total", metric_data=metric_data)

        assert "must be numeric" in str(exc_info.value).lower()

    def test_invalid_attributes_type_rejected(self):
        """Given invalid attributes type, then ValidationError raised."""
        from mca_sdk.validation.schema import MetricNamingConvention

        validator = MetricNamingConvention(
            model_category="internal", model_type="regression", strict=True
        )

        metric_data = {
            "name": "model.predictions_total",
            "value": 1.0,
            "attributes": "not_a_dict",  # Invalid: string instead of dict
        }

        with pytest.raises(Exception) as exc_info:
            validator.validate("model.predictions_total", metric_data=metric_data)

        assert "must be a dict" in str(exc_info.value).lower()


class TestAC2InvalidMetricNameRejection:
    """AC #2: Invalid metric name is rejected with helpful message."""

    def test_invalid_metric_name_rejected_strict_mode(self):
        """Given I record invalid metric name in strict mode, then ValidationError raised."""
        config = MCAConfig(
            service_name="test-service",
            model_id="test-model",
            team_name="test-team",
            model_category="internal",
            model_type="regression",
            strict_validation=True,
        )
        client = MCAClient(config=config)

        # Invalid name - should raise ValidationError in strict mode
        with pytest.raises(Exception) as exc_info:
            client.record_metric("invalid_metric_name", 1.0)

        # Verify it's a validation-related error
        assert "invalid" in str(exc_info.value).lower()
        client.shutdown()


class TestAC3StrictValidationMode:
    """AC #3: Strict validation mode fails immediately on violation."""

    def test_strict_mode_raises_on_invalid_metric(self):
        """Given strict mode, when schema violation, then ValidationError raised immediately."""
        config = MCAConfig(
            service_name="test-service",
            model_id="test-model",
            team_name="test-team",
            model_category="internal",
            model_type="regression",
            strict_validation=True,
        )
        client = MCAClient(config=config)

        # Record invalid metric (doesn't match model.* prefix pattern)
        # Should raise ValidationError immediately
        with pytest.raises(Exception) as exc_info:
            client.record_metric("custom_invalid_metric", 1.0)

        # Verify error contains helpful information
        error_msg = str(exc_info.value)
        assert "emms_model_*" in error_msg.lower()
        client.shutdown()


class TestAC4NonStrictValidationMode:
    """AC #4: Non-strict mode logs warning but exports metric."""

    def test_non_strict_mode_allows_custom_metrics(self):
        """Given non-strict mode, when custom metric, then warning logged."""
        config = MCAConfig(
            service_name="test-service",
            model_id="test-model",
            team_name="test-team",
            model_category="internal",
            model_type="regression",
            strict_validation=False,
        )
        client = MCAClient(config=config)

        # Should not raise (non-strict mode allows custom names)
        client.record_metric("custom_metric", 1.0)
        client.shutdown()

    def test_non_strict_mode_allows_legacy_hyphens(self):
        """Given non-strict mode, when legacy metric with hyphens, then allowed."""
        config = MCAConfig(
            service_name="test-service",
            model_id="test-model",
            team_name="test-team",
            model_category="internal",
            model_type="regression",
            strict_validation=False,
        )
        client = MCAClient(config=config)

        # Legacy metrics with hyphens should be allowed in non-strict mode
        client.record_metric("legacy-metric-name", 1.0)
        client.record_metric("my-custom-metric", 2.0)
        client.shutdown()


class TestAC6NamingConventionPrefixes:
    """AC #6: Metrics follow naming conventions by model type."""

    def test_model_prefix_for_internal_regression(self):
        """Given internal/regression, then model.* prefix required."""
        config = MCAConfig(
            service_name="test-service",
            model_id="test-model",
            team_name="test-team",
            model_category="internal",
            model_type="regression",
            strict_validation=True,
        )
        client = MCAClient(config=config)

        # Valid model.* metrics
        client.record_metric("emms_model_predictions_total", 1.0)
        client.record_metric("emms_model_latency_seconds", 0.5)
        client.record_metric("emms_model_accuracy_ratio", 0.95)

        # Nested namespaces should work (Issue #5 fix)
        client.record_metric("emms_model_layer1.predictions_total", 1.0)
        client.record_metric("emms_model_ensemble.accuracy_ratio", 0.98)

        client.shutdown()

    def test_genai_prefix_for_internal_generative(self):
        """Given internal/generative, then genai.* prefix required."""
        config = MCAConfig(
            service_name="test-service",
            model_id="test-model-genai",
            team_name="test-team",
            model_category="internal",
            model_type="generative",
            llm_provider="openai",
            llm_model="gpt-4",
            strict_validation=True,
        )
        client = MCAClient(config=config)

        # Valid genai.* metrics
        client.record_metric("genai.requests_total", 1.0)
        client.record_metric("genai.latency_seconds", 0.5)
        client.record_metric("genai.token_count", 150.0)
        client.shutdown()

    def test_agentic_prefix_schema_validation(self):
        """Test agentic.* prefix validation independent of resource attributes."""
        # Test the MetricNamingConvention directly (Story 3-14 scope)
        from mca_sdk.validation.schema import MetricNamingConvention

        # Strict mode: agentic.* patterns should be validated
        validator = MetricNamingConvention(
            model_category="internal", model_type="agentic", strict=True
        )

        # Valid agentic.* metrics should pass
        validator.validate("agentic.goals_total")
        validator.validate("agentic.reasoning_seconds")
        validator.validate("agentic.tool_count")

        # Invalid metrics should fail
        with pytest.raises(Exception):
            validator.validate("invalid_metric")

        # Wrong prefix should fail
        with pytest.raises(Exception):
            validator.validate("emms_model_predictions_total")

    def test_vendor_prefix_for_vendor_models(self):
        """Given vendor models, then vendor.* prefix required."""
        config = MCAConfig(
            service_name="test-service",
            model_id="test-model-vendor",
            team_name="test-team",
            model_category="vendor",
            model_type="regression",
            vendor_name="test-vendor",
            strict_validation=True,
        )
        client = MCAClient(config=config)

        # Valid vendor.* metrics
        client.record_metric("vendor.predictions_total", 1.0)
        client.record_metric("vendor.latency_seconds", 0.5)
        client.shutdown()
