"""Integration tests for telemetry attribute validation in MCAClient (Story 3.6).

Tests that attribute validation is properly integrated into record_prediction()
and other telemetry recording methods.
"""

import logging
import pytest

from mca_sdk import MCAClient
from mca_sdk.utils.exceptions import ValidationError
from mca_sdk.validation.telemetry_attributes import TelemetryAttributeValidator


class TestMCAClientAttributeValidation:
    """Test attribute validation integration with MCAClient."""

    def test_record_prediction_with_valid_attributes(self):
        """Test that record_prediction accepts valid attributes."""
        client = MCAClient(
            service_name="test-service",
            model_id="test-model",
            model_version="1.0.0",
            model_category="internal",
            model_type="regression",
            team_name="test-team",
            collector_endpoint="http://localhost:4318",
            strict_validation=True,
        )

        # Should not raise any exception
        client.record_prediction(
            input_data={"feature1": 1.0},
            output={"prediction": 0.5},
            latency=0.1,
            request_id="req-123",
            environment="production",
        )

        client.shutdown()

    def test_record_prediction_with_long_attribute_value_strict_mode(self):
        """Test that record_prediction raises ValidationError for long values in strict mode."""
        client = MCAClient(
            service_name="test-service",
            model_id="test-model",
            model_version="1.0.0",
            model_category="internal",
            model_type="regression",
            team_name="test-team",
            collector_endpoint="http://localhost:4318",
            strict_validation=True,
        )

        max_len = TelemetryAttributeValidator.MAX_VALUE_LENGTH
        long_value = "x" * (max_len + 1)

        with pytest.raises(ValidationError, match="value length.*exceeds maximum"):
            client.record_prediction(
                input_data={"feature1": 1.0},
                output={"prediction": 0.5},
                description=long_value,
            )

        client.shutdown()

    def test_record_prediction_with_long_attribute_value_lenient_mode(self):
        """Test that record_prediction truncates long values in lenient mode."""
        client = MCAClient(
            service_name="test-service",
            model_id="test-model",
            model_version="1.0.0",
            model_category="internal",
            model_type="regression",
            team_name="test-team",
            collector_endpoint="http://localhost:4318",
            strict_validation=False,
        )

        max_len = TelemetryAttributeValidator.MAX_VALUE_LENGTH
        long_value = "x" * (max_len + 1)

        # Should not raise exception, value should be truncated
        client.record_prediction(
            input_data={"feature1": 1.0},
            output={"prediction": 0.5},
            description=long_value,
        )

        client.shutdown()

    def test_record_prediction_with_invalid_attribute_name_strict_mode(self):
        """Test that record_prediction raises ValidationError for invalid names in strict mode."""
        client = MCAClient(
            service_name="test-service",
            model_id="test-model",
            model_version="1.0.0",
            model_category="internal",
            model_type="regression",
            team_name="test-team",
            collector_endpoint="http://localhost:4318",
            strict_validation=True,
        )

        # Attribute name starts with number (invalid)
        with pytest.raises(ValidationError, match="Invalid attribute name"):
            client.record_prediction(
                input_data={"feature1": 1.0},
                output={"prediction": 0.5},
                **{"123invalid": "value"},
            )

        client.shutdown()

    def test_record_prediction_with_invalid_attribute_name_lenient_mode(self):
        """Test that record_prediction skips invalid names in lenient mode."""
        client = MCAClient(
            service_name="test-service",
            model_id="test-model",
            model_version="1.0.0",
            model_category="internal",
            model_type="regression",
            team_name="test-team",
            collector_endpoint="http://localhost:4318",
            strict_validation=False,
        )

        # Should not raise exception, invalid name should be skipped
        client.record_prediction(
            input_data={"feature1": 1.0},
            output={"prediction": 0.5},
            valid_attr="kept",
            **{"123invalid": "dropped"},
        )

        client.shutdown()

    def test_record_prediction_with_too_many_attributes_strict_mode(self):
        """Test that record_prediction raises ValidationError for >100 attributes in strict mode."""
        client = MCAClient(
            service_name="test-service",
            model_id="test-model",
            model_version="1.0.0",
            model_category="internal",
            model_type="regression",
            team_name="test-team",
            collector_endpoint="http://localhost:4318",
            strict_validation=True,
        )

        # Create 101 attributes
        many_attrs = {f"attr_{i}": f"value_{i}" for i in range(101)}

        with pytest.raises(ValidationError, match="Attribute count .* exceeds maximum"):
            client.record_prediction(
                input_data={"feature1": 1.0}, output={"prediction": 0.5}, **many_attrs
            )

        client.shutdown()

    def test_record_prediction_with_too_many_attributes_lenient_mode(self):
        """Test that record_prediction drops excess attributes in lenient mode."""
        client = MCAClient(
            service_name="test-service",
            model_id="test-model",
            model_version="1.0.0",
            model_category="internal",
            model_type="regression",
            team_name="test-team",
            collector_endpoint="http://localhost:4318",
            strict_validation=False,
        )

        # Create 105 attributes
        many_attrs = {f"attr_{i}": f"value_{i}" for i in range(105)}

        # Should not raise exception, excess attributes should be dropped
        client.record_prediction(
            input_data={"feature1": 1.0}, output={"prediction": 0.5}, **many_attrs
        )

        client.shutdown()

    def test_record_prediction_validation_respects_config_setting(self):
        """Test that validation mode follows config.strict_validation setting."""
        # Strict mode client
        strict_client = MCAClient(
            service_name="test-service",
            model_id="test-model",
            model_version="1.0.0",
            model_category="internal",
            model_type="regression",
            team_name="test-team",
            collector_endpoint="http://localhost:4318",
            strict_validation=True,
        )

        # Lenient mode client
        lenient_client = MCAClient(
            service_name="test-service",
            model_id="test-model",
            model_version="1.0.0",
            model_category="internal",
            model_type="regression",
            team_name="test-team",
            collector_endpoint="http://localhost:4318",
            strict_validation=False,
        )

        max_len = TelemetryAttributeValidator.MAX_VALUE_LENGTH
        long_value = "x" * (max_len + 1)

        # Strict should raise
        with pytest.raises(ValidationError):
            strict_client.record_prediction(
                input_data={"feature1": 1.0},
                description=long_value,
            )

        # Lenient should not raise
        lenient_client.record_prediction(
            input_data={"feature1": 1.0},
            description=long_value,
        )

        strict_client.shutdown()
        lenient_client.shutdown()

    def test_record_prediction_with_mixed_validation_issues_lenient_mode(self):
        """Test that all validation issues are handled gracefully in lenient mode."""
        client = MCAClient(
            service_name="test-service",
            model_id="test-model",
            model_version="1.0.0",
            model_category="internal",
            model_type="regression",
            team_name="test-team",
            collector_endpoint="http://localhost:4318",
            strict_validation=False,
        )

        # Mix of valid, invalid names, and long values
        client.record_prediction(
            input_data={"feature1": 1.0},
            output={"prediction": 0.5},
            valid_attr="kept",
            long_attr="x" * 1025,  # Will be truncated
            **{"123invalid": "dropped"},  # Will be skipped
        )

        client.shutdown()

    @pytest.mark.skip(
        reason="SDK intentionally does NOT log input/output to prevent HIPAA violations"
    )
    def test_record_prediction_logs_input_output_data(self, caplog):
        """Test that input_data and output are NOT logged (security/HIPAA compliance).

        NOTE: This test is skipped because the SDK's security design explicitly
        states that PHI masking is application responsibility (CLAUDE.md line 176).
        The SDK must NOT automatically log input_data or output as they may contain PHI.
        """
        caplog.set_level(logging.INFO)

        # Note: input_data and output are only logged when debug_mode=True (v0.5.0+)
        client = MCAClient(
            service_name="test-service",
            model_id="test-model",
            model_version="1.0.0",
            model_category="internal",
            model_type="regression",
            team_name="test-team",
            collector_endpoint="http://localhost:4318",
            strict_validation=True,
            debug_mode=True,  # Required to log input_data and output
        )

        client.record_prediction(
            input_data={"feature1": 1.0, "feature2": 2.0},
            output={"prediction": 0.85, "confidence": 0.92},
            latency=0.15,
        )

        # SDK should NOT log input_data or output (HIPAA compliance requirement)
        prediction_logs = [r for r in caplog.records if "Prediction completed" in r.message]
        if prediction_logs:
            log_record = prediction_logs[0]
            assert not hasattr(log_record, "input_data"), "SDK must not log input_data (PHI risk)"
            assert not hasattr(log_record, "output"), "SDK must not log output (PHI risk)"

        client.shutdown()

    def test_validation_prevents_dos_via_large_object(self):
        """Test that validator blocks large objects BEFORE string conversion."""
        from mca_sdk.validation.telemetry_attributes import TelemetryAttributeValidator

        # Create object that reports massive size without creating massive data
        class MassiveObject(dict):
            def __sizeof__(self):
                return 10_000_000  # 10MB

            def __str__(self):
                return "x" * 10_000_000  # Would cause OOM if called

        # Strict mode should raise error BEFORE calling str()
        with pytest.raises(ValidationError, match="estimated size.*exceeds maximum"):
            TelemetryAttributeValidator.validate(
                {"dangerous": MassiveObject()},
                strict=True,
                max_value_length=TelemetryAttributeValidator.MAX_VALUE_LENGTH,
            )

    def test_validation_preserves_numeric_types(self):
        """Test that validator preserves int/float/bool instead of converting to strings."""
        from mca_sdk.validation.telemetry_attributes import TelemetryAttributeValidator

        attributes = {
            "int_value": 42,
            "float_value": 3.14,
            "bool_value": True,
            "string_value": "text",
        }

        validated = TelemetryAttributeValidator.validate(attributes, strict=True)

        # Types should be preserved
        assert isinstance(validated["int_value"], int)
        assert validated["int_value"] == 42
        assert isinstance(validated["float_value"], float)
        assert validated["float_value"] == 3.14
        assert isinstance(validated["bool_value"], bool)
        assert validated["bool_value"] is True
        assert isinstance(validated["string_value"], str)
        assert validated["string_value"] == "text"

    def test_validation_supports_arrays(self):
        """Test that validator supports arrays of primitives (OpenTelemetry arrays)."""
        from mca_sdk.validation.telemetry_attributes import TelemetryAttributeValidator

        attributes = {
            "tags": ["tag1", "tag2", "tag3"],
            "scores": [0.8, 0.9, 0.95],
            "flags": [True, False, True],
        }

        validated = TelemetryAttributeValidator.validate(attributes, strict=True)

        # Arrays should be preserved
        assert isinstance(validated["tags"], list)
        assert validated["tags"] == ["tag1", "tag2", "tag3"]
        assert isinstance(validated["scores"], list)
        assert validated["scores"] == [0.8, 0.9, 0.95]
        assert isinstance(validated["flags"], list)
        assert validated["flags"] == [True, False, True]

    def test_configurable_validation_limits(self):
        """Test that validation limits can be configured via MCAConfig."""
        # Client with custom limits
        client = MCAClient(
            service_name="test-service",
            model_id="test-model",
            model_version="1.0.0",
            model_category="internal",
            model_type="regression",
            team_name="test-team",
            collector_endpoint="http://localhost:4318",
            strict_validation=True,
            telemetry_max_attribute_length=100,  # Custom limit
            telemetry_max_attribute_count=10,  # Custom limit
        )

        # Value exceeding custom limit (100 chars) should raise
        with pytest.raises(ValidationError, match="value length.*exceeds maximum"):
            client.record_prediction(
                description="x" * 101,  # Exceeds custom 100-char limit
            )

        # 11 attributes exceeding custom count (10) should raise
        many_attrs = {f"attr_{i}": f"value_{i}" for i in range(11)}
        with pytest.raises(ValidationError, match="Attribute count.*exceeds maximum"):
            client.record_prediction(**many_attrs)

        client.shutdown()

    def test_validation_allows_underscore_prefix(self):
        """Test that validator allows underscore-prefixed attribute names."""
        from mca_sdk.validation.telemetry_attributes import TelemetryAttributeValidator

        attributes = {
            "_internal_id": "abc123",
            "_metadata": "test",
        }

        # Should not raise - underscore prefix is now allowed
        validated = TelemetryAttributeValidator.validate(attributes, strict=True)
        assert "_internal_id" in validated
        assert "_metadata" in validated
