"""
Tests for LWAI event transformation.

Verifies TimebackProfile → AggregationProfile conversion and field transformations.
"""

from timeback_caliper.resources.event_transformers import (
    LwaiEventTransformer,
    resolve_event_transformer,
    transform_for_lwai,
)


def test_transform_activity_event():
    """Test transformation of TimebackProfile ActivityEvent to LWAI AggregationProfile."""

    # Beyond TimebackProfile event
    beyond_envelope = {
        "sensor": "https://example.com",
        "sendTime": "2024-01-15T10:00:00Z",
        "dataVersion": "http://purl.imsglobal.org/ctx/caliper/v1p2",
        "data": [
            {
                "id": "urn:uuid:test-123",
                "type": "ActivityEvent",
                "profile": "TimebackProfile",
                "action": "Completed",
                "actor": {
                    "id": "https://example.com/users/student-456",
                    "type": "TimebackUser",
                    "email": "student@example.edu",
                },
                "object": {
                    "id": "https://example.com/activities/lesson-789",
                    "type": "TimebackActivityContext",
                    "name": "Math Lesson 1",
                    "subject": "Mathematics",
                    "course": {
                        "id": "https://example.com/courses/course-101",
                        "type": "CourseOffering",
                        "name": "Algebra I",
                    },
                    "app": {"name": "Membean", "type": "TimebackApp"},
                },
                "eventTime": "2024-01-15T10:00:00Z",
                "generated": {
                    "id": "urn:uuid:metrics-collection-1",
                    "type": "TimebackActivityMetricsCollection",
                    "items": [
                        {"type": "xpEarned", "value": 10},
                        {"type": "correctQuestions", "value": 8},
                        {"type": "totalQuestions", "value": 10},
                        {"type": "masteredUnits", "value": 1},
                    ],
                },
            }
        ],
    }

    # Transform
    lwai_envelope = transform_for_lwai(beyond_envelope)

    # Verify envelope structure preserved with LWAI datetime normalization
    assert lwai_envelope["sensor"] == beyond_envelope["sensor"]
    assert lwai_envelope["sendTime"] == "2024-01-15T10:00:00.000Z"
    assert lwai_envelope["dataVersion"] == beyond_envelope["dataVersion"]
    assert len(lwai_envelope["data"]) == 1

    event = lwai_envelope["data"][0]

    # Verify profile transformed
    assert event["profile"] == "AggregationProfile"

    # Verify actor transformed
    assert event["actor"]["type"] == "Person"
    assert "email" not in event["actor"]  # Email removed for LWAI strict mode

    # Verify object transformed
    assert event["object"]["type"] == "AssignableDigitalResource"
    assert event["object"]["name"] == "Math Lesson 1"
    assert "subject" not in event["object"]  # Subject removed for LWAI strict mode
    assert "isPartOf" in event["object"]
    assert event["object"]["isPartOf"]["type"] == "CourseOffering"

    # Verify app moved to extensions
    assert "extensions" in event["object"]
    assert event["object"]["extensions"]["app"]["name"] == "Membean"

    # Verify metrics transformed - type and PascalCase names
    assert event["generated"]["type"] == "ActivityMetricsCollection"
    assert event["generated"]["id"] == "urn:uuid:metrics-collection-1"
    assert "items" in event["generated"]

    # Build dict for easier assertion - transformer uses metricType/metricValue
    metrics_dict = {item["metricType"]: item["metricValue"] for item in event["generated"]["items"]}
    assert metrics_dict["XpEarned"] == 10
    assert metrics_dict["CorrectQuestions"] == 8
    assert metrics_dict["TotalQuestions"] == 10
    assert metrics_dict["MasteredUnits"] == 1
    assert event["eventTime"] == "2024-01-15T10:00:00.000Z"


def test_transform_preserves_standard_fields():
    """Test that standard Caliper fields are preserved."""

    beyond_envelope = {
        "sensor": "https://example.com",
        "sendTime": "2024-01-15T10:00:00Z",
        "dataVersion": "http://purl.imsglobal.org/ctx/caliper/v1p2",
        "data": [
            {
                "id": "urn:uuid:test-123",
                "type": "ActivityEvent",
                "profile": "TimebackProfile",
                "action": "Completed",
                "actor": {
                    "id": "https://example.com/users/1",
                    "type": "TimebackUser",
                    "email": "test@example.com",
                },
                "object": {
                    "id": "https://example.com/activities/1",
                    "type": "TimebackActivityContext",
                    "name": "Test",
                },
                "eventTime": "2024-01-15T10:00:00Z",
                "generated": {"type": "TimebackActivityMetricsCollection", "items": []},
            }
        ],
    }

    lwai_envelope = transform_for_lwai(beyond_envelope)
    event = lwai_envelope["data"][0]

    # Standard Caliper fields unchanged except LWAI datetime normalization
    assert event["id"] == "urn:uuid:test-123"
    assert event["type"] == "ActivityEvent"
    assert event["action"] == "Completed"
    assert event["eventTime"] == "2024-01-15T10:00:00.000Z"


def test_transform_empty_envelope():
    """Test transformation handles empty data gracefully."""

    envelope = {
        "sensor": "https://example.com",
        "sendTime": "2024-01-15T10:00:00Z",
        "dataVersion": "http://purl.imsglobal.org/ctx/caliper/v1p2",
        "data": [],
    }

    result = transform_for_lwai(envelope)
    assert result["data"] == []
    assert result["sendTime"] == "2024-01-15T10:00:00.000Z"


def test_transform_normalizes_fractional_and_offset_datetimes():
    """Datetime normalization trims/pads fractions and forces UTC Z suffix."""

    envelope = {
        "sensor": "https://example.com",
        "sendTime": "2024-01-15T10:00:00.789123+00:00",
        "dataVersion": "http://purl.imsglobal.org/ctx/caliper/v1p2",
        "data": [
            {
                "id": "urn:uuid:test-normalized",
                "type": "ActivityEvent",
                "profile": "TimebackProfile",
                "action": "Completed",
                "actor": {
                    "id": "https://example.com/users/1",
                    "type": "TimebackUser",
                    "email": "test@example.com",
                },
                "object": {
                    "id": "https://example.com/activities/1",
                    "type": "TimebackActivityContext",
                    "name": "Test",
                },
                "eventTime": "2024-01-15T10:00:00.7+00:00",
                "generated": {"type": "TimebackActivityMetricsCollection", "items": []},
            }
        ],
    }

    result = transform_for_lwai(envelope)
    event = result["data"][0]

    assert result["sendTime"] == "2024-01-15T10:00:00.789Z"
    assert event["eventTime"] == "2024-01-15T10:00:00.700Z"


def test_transform_converts_non_utc_offsets_before_forcing_z():
    """Non-UTC offsets are converted to UTC before replacing the suffix."""

    envelope = {
        "sensor": "https://example.com",
        "sendTime": "2024-01-15T10:00:00+05:30",
        "dataVersion": "http://purl.imsglobal.org/ctx/caliper/v1p2",
        "data": [
            {
                "id": "urn:uuid:test-offset",
                "type": "ActivityEvent",
                "profile": "TimebackProfile",
                "action": "Completed",
                "actor": {
                    "id": "https://example.com/users/1",
                    "type": "TimebackUser",
                    "email": "test@example.com",
                },
                "object": {
                    "id": "https://example.com/activities/1",
                    "type": "TimebackActivityContext",
                    "name": "Test",
                },
                "eventTime": "2024-01-15T10:00:00-02:30",
                "generated": {"type": "TimebackActivityMetricsCollection", "items": []},
            }
        ],
    }

    result = transform_for_lwai(envelope)
    event = result["data"][0]

    assert result["sendTime"] == "2024-01-15T04:30:00.000Z"
    assert event["eventTime"] == "2024-01-15T12:30:00.000Z"


def test_transform_multiple_events():
    """Test transformation handles multiple events in envelope."""

    envelope = {
        "sensor": "https://example.com",
        "sendTime": "2024-01-15T10:00:00Z",
        "dataVersion": "http://purl.imsglobal.org/ctx/caliper/v1p2",
        "data": [
            {
                "id": "urn:uuid:event-1",
                "type": "ActivityEvent",
                "profile": "TimebackProfile",
                "action": "Completed",
                "actor": {"id": "user-1", "type": "TimebackUser", "email": "user1@test.com"},
                "object": {
                    "id": "activity-1",
                    "type": "TimebackActivityContext",
                    "name": "Activity 1",
                },
                "eventTime": "2024-01-15T10:00:00Z",
                "generated": {"type": "TimebackActivityMetricsCollection", "items": []},
            },
            {
                "id": "urn:uuid:event-2",
                "type": "TimeSpentEvent",
                "profile": "TimebackProfile",
                "actor": {"id": "user-1", "type": "TimebackUser", "email": "user1@test.com"},
                "object": {
                    "id": "activity-1",
                    "type": "TimebackActivityContext",
                    "name": "Activity 1",
                },
                "eventTime": "2024-01-15T10:05:00Z",
                "metrics": {"type": "TimeSpentMetric", "value": 300},
            },
        ],
    }

    result = transform_for_lwai(envelope)

    assert len(result["data"]) == 2
    assert result["data"][0]["profile"] == "AggregationProfile"
    assert result["data"][1]["profile"] == "AggregationProfile"
    assert result["data"][0]["actor"]["type"] == "Person"
    assert result["data"][1]["actor"]["type"] == "Person"


def test_transform_activity_event_object_transforms():
    """ActivityEvent with TimebackActivityContext should transform object to AssignableDigitalResource."""

    envelope = {
        "sensor": "https://example.com",
        "sendTime": "2024-01-15T10:00:00Z",
        "dataVersion": "http://purl.imsglobal.org/ctx/caliper/v1p2",
        "data": [
            {
                "id": "urn:uuid:test-123",
                "type": "ActivityEvent",
                "profile": "TimebackProfile",
                "action": "Completed",
                "actor": {"id": "user-1", "type": "TimebackUser", "email": "test@test.com"},
                "object": {
                    "id": "activity-1",
                    "type": "TimebackActivityContext",
                    "name": "Math Lesson",
                    "subject": "Mathematics",
                },
                "eventTime": "2024-01-15T10:00:00Z",
                "generated": {"type": "TimebackActivityMetricsCollection", "items": []},
            }
        ],
    }

    result = transform_for_lwai(envelope)
    event = result["data"][0]

    # Object should be transformed
    assert event["object"]["type"] == "AssignableDigitalResource"
    assert event["object"]["name"] == "Math Lesson"
    assert "subject" not in event["object"]  # Subject removed for LWAI strict mode


def test_transform_time_spent_event_object_transforms():
    """TimeSpentEvent with TimebackActivityContext should transform object."""

    envelope = {
        "sensor": "https://example.com",
        "sendTime": "2024-01-15T10:00:00Z",
        "dataVersion": "http://purl.imsglobal.org/ctx/caliper/v1p2",
        "data": [
            {
                "id": "urn:uuid:test-456",
                "type": "TimeSpentEvent",
                "profile": "TimebackProfile",
                "actor": {"id": "user-1", "type": "TimebackUser", "email": "test@test.com"},
                "object": {
                    "id": "activity-1",
                    "type": "TimebackActivityContext",
                    "name": "Reading Activity",
                },
                "eventTime": "2024-01-15T10:00:00Z",
                "metrics": {"type": "TimeSpentMetric", "value": 300},
            }
        ],
    }

    result = transform_for_lwai(envelope)
    event = result["data"][0]

    # Object should be transformed
    assert event["object"]["type"] == "AssignableDigitalResource"
    assert event["object"]["name"] == "Reading Activity"


def test_transform_assessment_item_event_object_preserved():
    """AssessmentItemEvent with AssessmentItem should NOT transform object."""

    envelope = {
        "sensor": "https://example.com",
        "sendTime": "2024-01-15T10:00:00Z",
        "dataVersion": "http://purl.imsglobal.org/ctx/caliper/v1p2",
        "data": [
            {
                "id": "urn:uuid:test-789",
                "type": "AssessmentItemEvent",
                "profile": "AssessmentProfile",
                "action": "Completed",
                "actor": {"id": "user-1", "type": "Person", "email": "test@test.com"},
                "object": {
                    "id": "question-123",
                    "type": "AssessmentItem",
                    "name": "Question 1",
                    "questionText": "What is 2+2?",
                },
                "eventTime": "2024-01-15T10:00:00Z",
            }
        ],
    }

    result = transform_for_lwai(envelope)
    event = result["data"][0]

    # Object should be preserved - AssessmentItem NOT AssignableDigitalResource
    assert event["object"]["type"] == "AssessmentItem"
    assert event["object"]["name"] == "Question 1"
    assert event["object"]["questionText"] == "What is 2+2?"


def test_transform_session_event_object_preserved():
    """SessionEvent with SoftwareApplication should NOT transform object."""

    envelope = {
        "sensor": "https://example.com",
        "sendTime": "2024-01-15T10:00:00Z",
        "dataVersion": "http://purl.imsglobal.org/ctx/caliper/v1p2",
        "data": [
            {
                "id": "urn:uuid:session-1",
                "type": "SessionEvent",
                "profile": "SessionProfile",
                "action": "LoggedIn",
                "actor": {"id": "user-1", "type": "Person", "email": "test@test.com"},
                "object": {
                    "id": "app-mathacademy",
                    "type": "SoftwareApplication",
                    "name": "Math Academy",
                    "version": "2.0",
                },
                "eventTime": "2024-01-15T10:00:00Z",
            }
        ],
    }

    result = transform_for_lwai(envelope)
    event = result["data"][0]

    # Object should be preserved - SoftwareApplication NOT AssignableDigitalResource
    assert event["object"]["type"] == "SoftwareApplication"
    assert event["object"]["name"] == "Math Academy"
    assert event["object"]["version"] == "2.0"


def test_transform_unknown_event_type_object_preserved():
    """Unknown event type with TimebackActivityContext should NOT transform."""

    envelope = {
        "sensor": "https://example.com",
        "sendTime": "2024-01-15T10:00:00Z",
        "dataVersion": "http://purl.imsglobal.org/ctx/caliper/v1p2",
        "data": [
            {
                "id": "urn:uuid:custom-1",
                "type": "CustomEvent",
                "profile": "TimebackProfile",
                "action": "SomeAction",
                "actor": {"id": "user-1", "type": "TimebackUser", "email": "test@test.com"},
                "object": {
                    "id": "activity-1",
                    "type": "TimebackActivityContext",
                    "name": "Some Activity",
                },
                "eventTime": "2024-01-15T10:00:00Z",
            }
        ],
    }

    result = transform_for_lwai(envelope)
    event = result["data"][0]

    # Object should be preserved for unknown event types - safety first
    assert event["object"]["type"] == "TimebackActivityContext"
    assert event["object"]["name"] == "Some Activity"


def test_transform_assignable_event_object_transforms():
    """AssignableEvent with TimebackActivityContext should transform."""

    envelope = {
        "sensor": "https://example.com",
        "sendTime": "2024-01-15T10:00:00Z",
        "dataVersion": "http://purl.imsglobal.org/ctx/caliper/v1p2",
        "data": [
            {
                "id": "urn:uuid:assignable-1",
                "type": "AssignableEvent",
                "profile": "TimebackProfile",
                "action": "Started",
                "actor": {"id": "user-1", "type": "TimebackUser", "email": "test@test.com"},
                "object": {
                    "id": "assignment-1",
                    "type": "TimebackActivityContext",
                    "name": "Homework Assignment",
                },
                "eventTime": "2024-01-15T10:00:00Z",
            }
        ],
    }

    result = transform_for_lwai(envelope)
    event = result["data"][0]

    # Object should be transformed for AssignableEvent
    assert event["object"]["type"] == "AssignableDigitalResource"
    assert event["object"]["name"] == "Homework Assignment"


def test_transform_event_without_object_field():
    """Event without object field should not crash."""

    envelope = {
        "sensor": "https://example.com",
        "sendTime": "2024-01-15T10:00:00Z",
        "dataVersion": "http://purl.imsglobal.org/ctx/caliper/v1p2",
        "data": [
            {
                "id": "urn:uuid:no-object",
                "type": "CustomEvent",
                "profile": "TimebackProfile",
                "action": "SomeAction",
                "actor": {"id": "user-1", "type": "TimebackUser", "email": "test@test.com"},
                "eventTime": "2024-01-15T10:00:00Z",
            }
        ],
    }

    result = transform_for_lwai(envelope)
    event = result["data"][0]

    # Should not crash, object field simply not present
    assert "object" not in event
    assert event["profile"] == "AggregationProfile"  # Other transforms still apply


def test_resolve_event_transformer_returns_expected_implementation():
    """Resolver should return LWAI transformer only for LEARNWITH_AI."""
    assert isinstance(resolve_event_transformer("LEARNWITH_AI"), LwaiEventTransformer)
    assert resolve_event_transformer("BEYOND_AI") is None
    assert resolve_event_transformer(None) is None


def test_lwai_transformer_matches_function_output():
    """Class transformer should delegate exactly to transform_for_lwai."""
    envelope = {
        "sensor": "https://example.com",
        "sendTime": "2024-01-15T10:00:00Z",
        "dataVersion": "http://purl.imsglobal.org/ctx/caliper/v1p2",
        "data": [
            {
                "id": "urn:uuid:test-delegate",
                "type": "CustomEvent",
                "profile": "TimebackProfile",
                "action": "SomethingHappened",
                "actor": {"id": "user-1", "type": "TimebackUser", "email": "user1@test.com"},
                "eventTime": "2024-01-15T10:00:00Z",
            }
        ],
    }
    transformer = LwaiEventTransformer()

    assert transformer.transform(envelope) == transform_for_lwai(envelope)


def test_transform_non_timeback_user_actor_is_preserved():
    """Actor with type != 'TimebackUser' should not be transformed."""
    envelope = {
        "sensor": "https://example.com",
        "sendTime": "2024-01-15T10:00:00Z",
        "dataVersion": "http://purl.imsglobal.org/ctx/caliper/v1p2",
        "data": [
            {
                "id": "urn:uuid:test-person-actor",
                "type": "AssessmentItemEvent",
                "profile": "AssessmentProfile",
                "action": "Completed",
                "actor": {"id": "user-1", "type": "Person", "email": "student@test.com"},
                "object": {"id": "q-1", "type": "AssessmentItem", "name": "Q1"},
                "eventTime": "2024-01-15T10:00:00Z",
            }
        ],
    }

    result = transform_for_lwai(envelope)
    event = result["data"][0]

    # Actor type should be preserved as-is (not transformed to Person)
    assert event["actor"]["type"] == "Person"
    # email should also be preserved (not stripped)
    assert event["actor"]["email"] == "student@test.com"


def test_transform_string_iri_actor_is_preserved():
    """Actor that is a string IRI should not be transformed."""
    envelope = {
        "sensor": "https://example.com",
        "sendTime": "2024-01-15T10:00:00Z",
        "dataVersion": "http://purl.imsglobal.org/ctx/caliper/v1p2",
        "data": [
            {
                "id": "urn:uuid:test-iri-actor",
                "type": "AssessmentItemEvent",
                "profile": "AssessmentProfile",
                "action": "Completed",
                "actor": "urn:uuid:student-456",
                "object": {"id": "q-1", "type": "AssessmentItem"},
                "eventTime": "2024-01-15T10:00:00Z",
            }
        ],
    }

    result = transform_for_lwai(envelope)
    event = result["data"][0]

    # String actor should be passed through unchanged
    assert event["actor"] == "urn:uuid:student-456"


def test_transform_auto_injects_session_when_missing():
    """Session should be auto-injected as 'urn:tag:auto-attach' when not present."""
    envelope = {
        "sensor": "https://example.com",
        "sendTime": "2024-01-15T10:00:00Z",
        "dataVersion": "http://purl.imsglobal.org/ctx/caliper/v1p2",
        "data": [
            {
                "id": "urn:uuid:no-session",
                "type": "AssessmentItemEvent",
                "profile": "AssessmentProfile",
                "action": "Started",
                "actor": {"id": "user-1", "type": "Person"},
                "object": {"id": "q-1", "type": "AssessmentItem"},
                "eventTime": "2024-01-15T10:00:00Z",
            }
        ],
    }

    result = transform_for_lwai(envelope)
    event = result["data"][0]

    assert event["session"] == "urn:tag:auto-attach"


def test_transform_preserves_existing_session():
    """Session should not be overridden when already present."""
    envelope = {
        "sensor": "https://example.com",
        "sendTime": "2024-01-15T10:00:00Z",
        "dataVersion": "http://purl.imsglobal.org/ctx/caliper/v1p2",
        "data": [
            {
                "id": "urn:uuid:with-session",
                "type": "AssessmentItemEvent",
                "profile": "AssessmentProfile",
                "action": "Started",
                "actor": {"id": "user-1", "type": "Person"},
                "object": {"id": "q-1", "type": "AssessmentItem"},
                "session": "urn:uuid:my-real-session",
                "eventTime": "2024-01-15T10:00:00Z",
            }
        ],
    }

    result = transform_for_lwai(envelope)
    event = result["data"][0]

    assert event["session"] == "urn:uuid:my-real-session"
