# Copyright 2025 © BeeAI a Series of LF Projects, LLC
# SPDX-License-Identifier: Apache-2.0
from typing import Any, TypeAlias

from pydantic import BaseModel, Field


class BaseEvent(BaseModel):
    type: str


# request


class ResponsesRequestInputMessage(BaseEvent):
    role: str = Field(
        ...,
        description="The role of the message input. One of 'user', 'assistant', 'system', or 'developer'.",
        pattern="^(user|assistant|developer|system)$",
    )
    content: str | None = Field(
        None,
        description="Input to the model, used to generate a response. Can also contain previous assistant responses.",
    )
    type: str = Field("message", description="The type of the message input. Always 'message.")


class ResponsesRequestConversation(BaseModel):
    id: str = Field(..., description="The unique ID of the conversation.")


class ResponsesRequestBody(BaseModel):
    model: str = Field(description="Model ID used to generate the response")
    input: str | list[ResponsesRequestInputMessage] = Field(
        ..., description="Text inputs to the model, used to generate a response."
    )
    instructions: str | None = Field(
        None, description="A system (or developer) message inserted into the model's context."
    )
    conversation: str | ResponsesRequestConversation | None = Field(
        None,
        description="The conversation that this response belongs to. Items from this conversation are prepended "
        "to input_items for this response request. Input items and output items from this response "
        "are automatically added to this conversation after this response completes.",
    )
    stream: bool | None = Field(
        False,
        description="If set to true, the model response data will be streamed to the client as it is generated "
        "using server-sent events.",
    )


# response


class ResponsesCustomToolCallOutput(BaseEvent):
    type: str = Field("custom_tool_call", description="The type of the custom tool call. Always 'custom_tool_call'.")
    id: str = Field(..., description="The unique ID of the custom tool call in the platform.")
    name: str = Field(..., description="The name of the custom tool being called.")
    input: str = Field(..., description="The input for the custom tool call generated by the model.")
    call_id: str = Field(..., description="An identifier used to map this custom tool call to a tool call output.")


class ResponsesReasoningSummary(BaseEvent):
    type: str = Field("summary_text", description="The type of the object. Always 'summary_text.")
    text: str = Field(..., description="A summary of the reasoning output from the model so far.")


class ResponsesReasoningContent(BaseEvent):
    type: str = Field("reasoning_text", description="The type of the reasoning text. Always 'reasoning_text'.")
    text: str = Field(..., description="The reasoning text from the model.")


class ResponsesReasoningOutput(BaseEvent):
    type: str = Field("reasoning", description="The type of the object. Always 'reasoning'.")
    id: str = Field(..., description="The unique identifier of the reasoning content.")
    status: str = Field(
        ..., description="The status of the message input. One of 'in_progress', 'completed', or 'incomplete'."
    )
    summary: ResponsesReasoningSummary | None = Field(None, description="Reasoning summary content.")
    content: ResponsesReasoningContent | None = Field(None, description="Reasoning text content.")


class ResponsesMessageContent(BaseEvent):
    type: str = Field("output_text", description="The type of the output text. Always output_text.")
    text: str = Field(..., description="The text output from the model.")


class ResponsesMessageOutput(BaseEvent):
    type: str = Field("message", description="The type of the output message")
    id: str = Field(..., description="The unique ID of the output message.")
    status: str = Field(
        "in_progress",
        description="The status of the message input. One of 'in_progress', 'completed', or 'incomplete'.",
    )
    role: str = Field("assistant", description="The role of the output message. Always 'assistant'.")
    content: list[ResponsesMessageContent] = Field([], description="The content of the output message.")


class ResponsesUsage(BaseModel):
    input_tokens: int = Field(..., description="The number of input tokens.")
    output_tokens: int = Field(..., description="The number of output tokens.")
    total_tokens: int = Field(..., description="The total number of tokens used.")


ResponsesResponseOutput: TypeAlias = ResponsesMessageOutput | ResponsesReasoningOutput | ResponsesCustomToolCallOutput


class ResponsesResponse(BaseModel):
    id: str = Field(..., description="Unique identifier for this Response.")
    object: str = Field(
        "response",
        description="The type of object returned, should be 'response'",
    )
    created: int = Field(..., description="Unix timestamp (in seconds) of when this Response was created.")
    status: str = Field(
        "queued",
        description="The status of the response generation. One of 'completed', 'failed', 'in_progress', "
        "'cancelled', 'queued', or 'incomplete'",
    )
    error: str | None = Field(None, description="An error object returned when the model fails to generate a Response.")
    instructions: str | None = Field(
        None, description="A system (or developer) message inserted into the model's context."
    )
    model: str = Field(..., description="Model ID used to generate the response.")
    output: list[ResponsesMessageOutput | ResponsesReasoningOutput | ResponsesCustomToolCallOutput] | None = Field(
        None, description="An array of content items generated by the model."
    )
    usage: ResponsesUsage | None = Field(
        None,
        description="Represents token usage details including input tokens, output tokens, "
        "a breakdown of output tokens, and the total tokens used.",
    )


# stream


class ResponsesStreamResponse(BaseEvent):
    response: ResponsesResponse = Field(..., description="The response that was created.")
    sequence_number: int = Field(..., description="The sequence number for this event.")
    type: str = Field("response", description="The type of the event.")


class ResponsesStreamResponseCreated(ResponsesStreamResponse):
    type: str = "response.created"


class ResponsesStreamResponseInProgress(ResponsesStreamResponse):
    type: str = "response.in_progress"


class ResponsesStreamResponseCompleted(ResponsesStreamResponse):
    type: str = "response.completed"


class ResponsesStreamResponseFailed(ResponsesStreamResponse):
    type: str = "response.failed"


class ResponsesStreamResponseIncomplete(ResponsesStreamResponse):
    type: str = "response.incomplete"


class ResponsesStreamOutputItemAdded(BaseEvent):
    item: ResponsesMessageOutput | ResponsesReasoningOutput | ResponsesCustomToolCallOutput = Field(
        ..., description="The output item."
    )
    output_index: int = Field(..., description="The index of the output item.")
    sequence_number: int = Field(..., description="The sequence number for this event.")
    type: str = Field("response.output_item.added", description="The type of the event.")


class ResponsesStreamOutputItemDone(ResponsesStreamOutputItemAdded):
    type: str = "response.output_item.done"


class ResponsesStreamPartOutputText(BaseEvent):
    text: str = Field(..., description="The text output from the model.")
    type: str = Field("response.output_text.done", description="The type of the output text.")
    annotations: list[Any] = Field([], description="The annotations of the text output.")


class ResponsesStreamPartReasoningText(BaseEvent):
    text: str = Field(..., description="The reasoning text from the model.")
    type: str = Field("reasoning", description="The type of the reasoning text.")


class ResponsesStreamPartRefusal(BaseEvent):
    refusal: str = Field(..., description="The refusal explanation from the model.")
    type: str = Field("refusal", description="The type of the refusal.")


class ResponsesStreamContentPartAdded(BaseEvent):
    content_index: int = Field(0, description="The index of the content part.")
    item_id: str = Field(..., description="The ID of the output item that the content part was added to.")
    output_index: int = Field(0, description="The index of the output item that the content part was added to.")
    part: ResponsesStreamPartOutputText | ResponsesStreamPartReasoningText | ResponsesStreamPartRefusal = Field(
        ..., description="The content part."
    )
    sequence_number: int = Field(..., description="The sequence number for this event.")
    type: str = Field("response.content_part.added", description="The type of the event.")


class ResponsesStreamContentPartDone(ResponsesStreamContentPartAdded):
    type: str = "response.content_part.done"


class ResponsesStreamOutputTextDelta(BaseEvent):
    content_index: int = Field(0, description="The index of the content part that the text delta was added to.")
    delta: str = Field(..., description="The text delta that was added.")
    item_id: str = Field(..., description="The ID of the output item that the text delta was added to.")
    output_index: int = Field(..., description="The index of the output item that the text delta was added to.")
    sequence_number: int = Field(..., description="The sequence number for this event.")
    type: str = Field("response.output_text.delta", description="The type of the event.")


class ResponsesStreamOutputTextDone(BaseEvent):
    content_index: int = Field(0, description="The index of the content part that the text delta was added to.")
    text: str = Field(..., description="The text content that is finalized.")
    item_id: str = Field(..., description="The ID of the output item that the text delta was added to.")
    output_index: int = Field(..., description="The index of the output item that the text delta was added to.")
    sequence_number: int = Field(..., description="The sequence number for this event.")
    type: str = Field("response.output_text.done", description="The type of the event.")


class ResponsesStreamRefusalDelta(ResponsesStreamOutputTextDelta):
    type: str = "response.refusal.delta"


class ResponsesStreamRefusalDone(ResponsesStreamOutputTextDone):
    type: str = "response.refusal.done"


class ResponsesStreamCustomToolCallDelta(BaseEvent):
    delta: str = Field(..., description="The incremental input data (delta) for the custom tool call.")
    item_id: str = Field(..., description="Unique identifier for the API item associated with this event.")
    output_index: int = Field(..., description="The index of the output item that the text delta was added to.")
    sequence_number: int = Field(..., description="The sequence number for this event.")
    type: str = Field("response.custom_tool_call_input.delta", description="The type of the event.")


class ResponsesStreamCustomToolCallDone(BaseEvent):
    input: str = Field(..., description="The complete input data for the custom tool call.")
    item_id: str = Field(..., description="Unique identifier for the API item associated with this event.")
    output_index: int = Field(..., description="The index of the output item that the text delta was added to.")
    sequence_number: int = Field(..., description="The sequence number for this event.")
    type: str = Field("response.custom_tool_call_input.done", description="The type of the event.")


class ResponsesStreamError(BaseEvent):
    code: str = Field(..., description="The error code.")
    message: str = Field(..., description="The error message.")
    param: str = Field(..., description="The error parameter.")
    sequence_number: int = Field(..., description="The sequence number for this event.")
    type: str = Field("error", description="The type of the event.")
