"""
Event Factory Functions

Pure factory functions for creating Caliper events.
Use these when you want to batch multiple events into a single envelope.
"""

from __future__ import annotations

import uuid

from timeback_common import utc_iso_timestamp

from .constants import QUESTION_RESULT_SCORE_TYPE
from .types.timeback import (
    ActivityCompletedEvent,
    ActivityCompletedInput,
    AssessmentItemEvent,
    QuestionAnsweredInput,
    QuestionGradedInput,
    QuestionGradeEvent,
    QuestionSeenInput,
    TimebackActivityMetricsCollection,
    TimebackTimeSpentMetricsCollection,
    TimeSpentEvent,
    TimeSpentInput,
)


def create_activity_event(input: ActivityCompletedInput) -> ActivityCompletedEvent:
    """
    Create an ActivityEvent from input.

    Pure factory function - creates the event object without sending.
    Use this when you want to batch multiple events into a single envelope.

    Args:
        input: Activity completion data

    Returns:
        A fully-formed ActivityCompletedEvent ready to send

    Example:
        ```python
        from timeback_caliper import create_activity_event, create_time_spent_event

        activity_event = create_activity_event(ActivityCompletedInput(
            actor=TimebackUser(id="...", type="TimebackUser", email="student@example.edu"),
            object=TimebackActivityContext(
                id="...",
                type="TimebackActivityContext",
                subject="Math",
                app=TimebackApp(name="My App"),
            ),
            metrics=[
                TimebackActivityMetric(type="totalQuestions", value=10),
                TimebackActivityMetric(type="correctQuestions", value=8),
            ],
        ))

        time_spent_event = create_time_spent_event(TimeSpentInput(
            actor=TimebackUser(id="...", type="TimebackUser", email="student@example.edu"),
            object=TimebackActivityContext(...),
            metrics=[TimeSpentMetric(type="active", value=1800)],
        ))

        # Send both in one envelope
        await client.events.send(sensor, [activity_event, time_spent_event])
        ```
    """
    event_id = input.id or f"urn:uuid:{uuid.uuid4()}"
    metrics_id = input.metrics_id or f"urn:uuid:{uuid.uuid4()}"

    event_kwargs: dict = {
        "id": event_id,
        "actor": input.actor,
        "object": input.object,
        "eventTime": input.event_time or utc_iso_timestamp(),
        "generated": TimebackActivityMetricsCollection(
            id=metrics_id,
            items=input.metrics,
            attempt=input.attempt,
            extensions=input.generated_extensions,
        ),
        "extensions": input.extensions,
    }
    if input.ed_app is not None:
        event_kwargs["ed_app"] = input.ed_app
    if input.session is not None:
        event_kwargs["session"] = input.session

    return ActivityCompletedEvent(**event_kwargs)


def create_time_spent_event(input: TimeSpentInput) -> TimeSpentEvent:
    """
    Create a TimeSpentEvent from input.

    Pure factory function - creates the event object without sending.
    Use this when you want to batch multiple events into a single envelope.

    Args:
        input: Time spent data

    Returns:
        A fully-formed TimeSpentEvent ready to send

    Example:
        ```python
        from timeback_caliper import create_time_spent_event

        event = create_time_spent_event(TimeSpentInput(
            actor=TimebackUser(id="...", type="TimebackUser", email="student@example.edu"),
            object=TimebackActivityContext(
                id="...",
                type="TimebackActivityContext",
                subject="Reading",
                app=TimebackApp(name="My App"),
            ),
            metrics=[
                TimeSpentMetric(type="active", value=1800),
                TimeSpentMetric(type="inactive", value=300),
            ],
        ))

        await client.events.send(sensor, [event])
        ```
    """
    event_id = input.id or f"urn:uuid:{uuid.uuid4()}"
    metrics_id = input.metrics_id or f"urn:uuid:{uuid.uuid4()}"

    event_kwargs: dict = {
        "id": event_id,
        "actor": input.actor,
        "object": input.object,
        "eventTime": input.event_time or utc_iso_timestamp(),
        "generated": TimebackTimeSpentMetricsCollection(
            id=metrics_id,
            items=input.metrics,
        ),
        "extensions": input.extensions,
    }
    if input.ed_app is not None:
        event_kwargs["ed_app"] = input.ed_app
    if input.session is not None:
        event_kwargs["session"] = input.session

    return TimeSpentEvent(**event_kwargs)


def create_question_seen_event(input: QuestionSeenInput) -> AssessmentItemEvent:
    """
    Create a Question Seen event (AssessmentItemEvent.Started).

    Fired when a question is presented to the student.

    Args:
        input: Minimal question seen data

    Returns:
        A fully-formed AssessmentItemEvent ready to send

    Example:
        ```python
        from timeback_caliper import create_question_seen_event
        from timeback_caliper.types.timeback import QuestionObjectInput, QuestionSeenInput

        event = create_question_seen_event(QuestionSeenInput(
            actor="urn:uuid:student-123",
            object=QuestionObjectInput(id="urn:uuid:q-456", name="What is 2+2?"),
            ed_app="https://myapp.example.com",
        ))

        await client.events.send(sensor_id, [event])
        ```
    """
    event_id = input.id or f"urn:uuid:{uuid.uuid4()}"

    # Build object dict with type set to AssessmentItem
    obj_dict = input.object.model_dump(mode="json", by_alias=True, exclude_none=True)
    obj_dict["type"] = "AssessmentItem"

    kwargs: dict = {
        "id": event_id,
        "action": "Started",
        "actor": input.actor,
        "object": obj_dict,
        "eventTime": input.event_time or utc_iso_timestamp(),
    }
    if input.ed_app is not None:
        kwargs["ed_app"] = input.ed_app
    if input.session is not None:
        kwargs["session"] = input.session
    if input.extensions is not None:
        kwargs["extensions"] = input.extensions

    return AssessmentItemEvent(**kwargs)


def create_question_answered_event(input: QuestionAnsweredInput) -> AssessmentItemEvent:
    """
    Create a Question Answered event (AssessmentItemEvent.Completed).

    Fired when a student submits an answer to a question.

    Args:
        input: Minimal question answered data

    Returns:
        A fully-formed AssessmentItemEvent ready to send

    Example:
        ```python
        from timeback_caliper import create_question_answered_event
        from timeback_caliper.types.timeback import (
            QuestionObjectInput, QuestionAnsweredInput, ResponseGeneratedInput
        )

        event = create_question_answered_event(QuestionAnsweredInput(
            actor="urn:uuid:student-123",
            object=QuestionObjectInput(id="urn:uuid:q-456", name="What is 2+2?"),
            ed_app="https://myapp.example.com",
            generated=ResponseGeneratedInput(
                id="urn:uuid:response-789",
                attempt="urn:uuid:attempt-101",
            ),
        ))

        await client.events.send(sensor_id, [event])
        ```
    """
    event_id = input.id or f"urn:uuid:{uuid.uuid4()}"

    # Build object dict with type set to AssessmentItem
    obj_dict = input.object.model_dump(mode="json", by_alias=True, exclude_none=True)
    obj_dict["type"] = "AssessmentItem"

    # Build generated dict with type set to Response
    generated_dict = None
    if input.generated is not None:
        generated_dict = input.generated.model_dump(mode="json", by_alias=True, exclude_none=True)
        generated_dict["type"] = "Response"

    kwargs: dict = {
        "id": event_id,
        "action": "Completed",
        "actor": input.actor,
        "object": obj_dict,
        "eventTime": input.event_time or utc_iso_timestamp(),
    }
    if input.ed_app is not None:
        kwargs["ed_app"] = input.ed_app
    if generated_dict is not None:
        kwargs["generated"] = generated_dict
    if input.session is not None:
        kwargs["session"] = input.session
    if input.extensions is not None:
        kwargs["extensions"] = input.extensions

    return AssessmentItemEvent(**kwargs)


def create_question_graded_event(input: QuestionGradedInput) -> QuestionGradeEvent:
    """
    Create a Question Graded event (GradeEvent.Graded).

    Fired when a question response is scored. The Score entity is auto-filled
    with type="Score" and score_type="QUESTION_RESULT".

    Args:
        input: Minimal question graded data

    Returns:
        A fully-formed QuestionGradeEvent ready to send

    Example:
        ```python
        from timeback_caliper import create_question_graded_event
        from timeback_caliper.types.timeback import QuestionGradedInput, ScoreGeneratedInput

        event = create_question_graded_event(QuestionGradedInput(
            actor="urn:uuid:student-123",
            object="urn:uuid:attempt-101",
            generated=ScoreGeneratedInput(score_given=1, max_score=1, attempt="urn:uuid:attempt-101"),
            ed_app="https://myapp.example.com",
        ))

        await client.events.send(sensor_id, [event])
        ```
    """
    event_id = input.id or f"urn:uuid:{uuid.uuid4()}"

    # Build score dict: extract id (may be None), then add type and scoreType
    score_data = input.generated.model_dump(mode="json", by_alias=True, exclude_none=True)
    score_id = score_data.pop("id", None)
    score_data["id"] = score_id or f"urn:uuid:{uuid.uuid4()}"
    score_data["type"] = "Score"
    score_data["scoreType"] = QUESTION_RESULT_SCORE_TYPE

    kwargs: dict = {
        "id": event_id,
        "actor": input.actor,
        "object": input.object,
        "generated": score_data,
        "eventTime": input.event_time or utc_iso_timestamp(),
    }
    if input.ed_app is not None:
        kwargs["ed_app"] = input.ed_app
    if input.session is not None:
        kwargs["session"] = input.session
    if input.extensions is not None:
        kwargs["extensions"] = input.extensions

    return QuestionGradeEvent(**kwargs)


__all__ = [
    "create_activity_event",
    "create_question_answered_event",
    "create_question_graded_event",
    "create_question_seen_event",
    "create_time_spent_event",
]
