# Copyright (c) Microsoft. All rights reserved.

"""Tests for Purview models and serialization."""

from agent_framework_purview._models import (
    Activity,
    ActivityMetadata,
    ContentToProcess,
    DeviceMetadata,
    IntegratedAppMetadata,
    OperatingSystemSpecifications,
    PolicyLocation,
    ProcessContentRequest,
    ProcessContentResponse,
    ProcessConversationMetadata,
    ProtectedAppMetadata,
    ProtectionScopeActivities,
    ProtectionScopesRequest,
    ProtectionScopesResponse,
    PurviewTextContent,
    deserialize_flag,
    serialize_flag,
)


class TestFlagOperations:
    """Test flag serialization and deserialization operations."""

    def test_protection_scope_activities_flag_combination(self) -> None:
        """Test combining flags."""
        combined = ProtectionScopeActivities.UPLOAD_TEXT | ProtectionScopeActivities.UPLOAD_FILE
        assert combined.value == 3
        assert ProtectionScopeActivities.UPLOAD_TEXT in combined
        assert ProtectionScopeActivities.UPLOAD_FILE in combined

    def test_deserialize_flag_with_string(self) -> None:
        """Test deserializing flag from comma-separated string."""
        mapping = {
            "uploadText": ProtectionScopeActivities.UPLOAD_TEXT,
            "uploadFile": ProtectionScopeActivities.UPLOAD_FILE,
        }

        result = deserialize_flag("uploadText,uploadFile", mapping, ProtectionScopeActivities)
        assert result is not None
        assert ProtectionScopeActivities.UPLOAD_TEXT in result
        assert ProtectionScopeActivities.UPLOAD_FILE in result

    def test_deserialize_flag_with_none(self) -> None:
        """Test deserializing None returns None."""
        mapping = {"uploadText": ProtectionScopeActivities.UPLOAD_TEXT}
        result = deserialize_flag(None, mapping, ProtectionScopeActivities)
        assert result is None

    def test_serialize_flag_with_none(self) -> None:
        """Test serializing None returns None."""
        result = serialize_flag(None, [])
        assert result is None

    def test_serialize_flag_with_values(self) -> None:
        """Test serializing flag with values."""
        flag = ProtectionScopeActivities.UPLOAD_TEXT | ProtectionScopeActivities.UPLOAD_FILE
        ordered = [
            ("uploadText", ProtectionScopeActivities.UPLOAD_TEXT),
            ("uploadFile", ProtectionScopeActivities.UPLOAD_FILE),
        ]
        result = serialize_flag(flag, ordered)
        assert result == "uploadText,uploadFile"


class TestComplexModels:
    """Test complex models with nested structures."""

    def test_content_to_process_with_nested_structures(self) -> None:
        """Test ContentToProcess with all nested structures."""
        text_content = PurviewTextContent(data="Test")
        metadata = ProcessConversationMetadata(
            identifier="msg-1",
            content=text_content,
            name="Test",
            is_truncated=False,
        )

        activity_meta = ActivityMetadata(activity=Activity.UPLOAD_TEXT)
        device_meta = DeviceMetadata(
            operating_system_specifications=OperatingSystemSpecifications(
                operating_system_platform="Windows", operating_system_version="10"
            )
        )
        integrated_app = IntegratedAppMetadata(name="App", version="1.0")
        location = PolicyLocation(data_type="microsoft.graph.policyLocationApplication", value="app-id")
        protected_app = ProtectedAppMetadata(name="Protected", version="1.0", application_location=location)

        content = ContentToProcess(
            content_entries=[metadata],
            activity_metadata=activity_meta,
            device_metadata=device_meta,
            integrated_app_metadata=integrated_app,
            protected_app_metadata=protected_app,
        )

        assert len(content.content_entries) == 1
        assert content.activity_metadata.activity == Activity.UPLOAD_TEXT
        os_specs = content.device_metadata.operating_system_specifications
        assert os_specs is not None
        assert os_specs.operating_system_platform == "Windows"
        assert content.integrated_app_metadata.name == "App"
        assert content.protected_app_metadata.name == "Protected"


class TestRequestResponseSerialization:
    """Test request/response serialization with aliases."""

    def test_protection_scopes_request_serialization(self) -> None:
        """Test ProtectionScopesRequest serializes activities correctly."""
        location = PolicyLocation(data_type="microsoft.graph.policyLocationApplication", value="app-id")

        request = ProtectionScopesRequest(
            user_id="user-123",
            tenant_id="tenant-456",
            activities=ProtectionScopeActivities.UPLOAD_TEXT | ProtectionScopeActivities.UPLOAD_FILE,
            locations=[location],
        )

        dumped = request.model_dump(by_alias=True, exclude_none=True, mode="json")

        assert "activities" in dumped
        assert isinstance(dumped["activities"], str)
        assert "uploadText" in dumped["activities"]


class TestModelDeserialization:
    """Test model deserialization from API responses."""

    def test_protection_scopes_response_deserialization(self) -> None:
        """Test ProtectionScopesResponse deserializes 'value' to 'scopes'."""
        api_data = {
            "scopeIdentifier": "scope-123",
            "value": [
                {
                    "activities": "uploadText,downloadText",
                    "locations": [{"@odata.type": "location.type", "value": "/path"}],
                    "policyActions": [{"action": "warn", "restrictionAction": "blockAccess"}],
                    "executionMode": "evaluateInline",
                }
            ],
        }

        response = ProtectionScopesResponse.model_validate(api_data)

        assert response.scope_identifier == "scope-123"
        assert response.scopes is not None
        assert len(response.scopes) == 1
        assert response.scopes[0].execution_mode == "evaluateInline"

    def test_process_content_response_deserialization(self) -> None:
        """Test ProcessContentResponse deserializes aliased fields correctly."""
        api_data = {
            "id": "response-123",
            "protectionScopeState": "blocked",
            "policyActions": [{"action": "block", "restrictionAction": "blockAccess"}],
        }

        response = ProcessContentResponse.model_validate(api_data)

        assert response.id == "response-123"
        assert response.protection_scope_state == "blocked"
        assert response.policy_actions is not None
        assert len(response.policy_actions) == 1

    def test_content_serialization_uses_aliases(self) -> None:
        """Test ContentToProcess serializes with camelCase aliases."""
        text_content = PurviewTextContent(data="Test")
        metadata = ProcessConversationMetadata(
            identifier="msg-1",
            content=text_content,
            name="Test",
            is_truncated=False,
        )

        activity_meta = ActivityMetadata(activity=Activity.UPLOAD_TEXT)
        device_meta = DeviceMetadata(
            operating_system_specifications=OperatingSystemSpecifications(
                operating_system_platform="Windows", operating_system_version="10"
            )
        )
        integrated_app = IntegratedAppMetadata(name="App", version="1.0")
        location = PolicyLocation(data_type="microsoft.graph.policyLocationApplication", value="app-id")
        protected_app = ProtectedAppMetadata(name="Protected", version="1.0", application_location=location)

        content = ContentToProcess(
            content_entries=[metadata],
            activity_metadata=activity_meta,
            device_metadata=device_meta,
            integrated_app_metadata=integrated_app,
            protected_app_metadata=protected_app,
        )

        dumped = content.model_dump(by_alias=True, exclude_none=True, mode="json")

        assert "contentEntries" in dumped
        assert "activityMetadata" in dumped
        assert "deviceMetadata" in dumped
        assert "integratedAppMetadata" in dumped
        assert "protectedAppMetadata" in dumped

    def test_process_content_request_excludes_private_fields(self) -> None:
        """Test ProcessContentRequest excludes private fields when serializing."""
        text_content = PurviewTextContent(data="Test")
        metadata = ProcessConversationMetadata(
            identifier="msg-1",
            content=text_content,
            name="Test",
            is_truncated=False,
        )

        activity_meta = ActivityMetadata(activity=Activity.UPLOAD_TEXT)
        device_meta = DeviceMetadata(
            operating_system_specifications=OperatingSystemSpecifications(
                operating_system_platform="Windows", operating_system_version="10"
            )
        )
        integrated_app = IntegratedAppMetadata(name="App", version="1.0")
        location = PolicyLocation(data_type="microsoft.graph.policyLocationApplication", value="app-id")
        protected_app = ProtectedAppMetadata(name="Protected", version="1.0", application_location=location)

        content = ContentToProcess(
            content_entries=[metadata],
            activity_metadata=activity_meta,
            device_metadata=device_meta,
            integrated_app_metadata=integrated_app,
            protected_app_metadata=protected_app,
        )

        request = ProcessContentRequest(
            content_to_process=content,
            user_id="user-123",
            tenant_id="tenant-456",
            correlation_id="corr-789",
        )

        dumped = request.model_dump(by_alias=True, exclude_none=True, mode="json")

        assert "user_id" in dumped
        assert "tenant_id" in dumped
        assert "correlation_id" not in dumped
        assert "contentToProcess" in dumped
