package types

import (
	"encoding/json"
	"errors"
	"fmt"
)

type MessageItemType string

const (
	MessageItemTypeMessage             MessageItemType = "message"
	MessageItemTypeFunctionCall        MessageItemType = "function_call"
	MessageItemTypeFunctionCallOutput  MessageItemType = "function_call_output"
	MessageItemTypeMCPApprovalResponse MessageItemType = "mcp_approval_response"
	MessageItemTypeMCPListTools        MessageItemType = "mcp_list_tools"
	MessageItemTypeMCPCall             MessageItemType = "mcp_call"
	MessageItemTypeMCPApprovalRequest  MessageItemType = "mcp_approval_request"
)

type MessageContentType string

const (
	MessageContentTypeText        MessageContentType = "text"
	MessageContentTypeAudio       MessageContentType = "audio"
	MessageContentTypeTranscript  MessageContentType = "transcript"
	MessageContentTypeInputText   MessageContentType = "input_text"
	MessageContentTypeInputAudio  MessageContentType = "input_audio"
	MessageContentTypeInputImage  MessageContentType = "input_image"
	MessageContentTypeOutputText  MessageContentType = "output_text"
	MessageContentTypeOutputAudio MessageContentType = "output_audio"
)

type MessageContentText struct {
	Text string `json:"text,omitempty"`
}

type MessageContentAudio struct {
	Type  MessageContentType `json:"type,omitempty"`
	Audio string             `json:"audio,omitempty"`
}

type MessageContentTranscript struct {
	Type       MessageContentType `json:"type,omitempty"`
	Transcript string             `json:"transcript,omitempty"`
}

type MessageContentImage struct {
	Type     MessageContentType `json:"type,omitempty"`
	ImageURL string             `json:"image_url,omitempty"`
	Detail   ImageDetail        `json:"detail,omitempty"`
}

type MessageContentSystem MessageContentText

type MessageItemSystem struct {
	// The unique ID of the item. This may be provided by the client or generated by the server.
	ID string `json:"id,omitempty"`

	// The content of the message.
	Content []MessageContentSystem `json:"content,omitempty"`

	// Identifier for the API object being returned - always realtime.item. Optional when creating a new item.
	Object string `json:"object,omitempty"`

	// The status of the item. Has no effect on the conversation.
	Status ItemStatus `json:"status,omitempty"`
}

func (m MessageItemSystem) MessageItemType() MessageItemType {
	return MessageItemTypeMessage
}

func (m MessageItemSystem) Role() MessageRole {
	return MessageRoleSystem
}

func (m MessageItemSystem) MarshalJSON() ([]byte, error) {
	type typeAlias MessageItemSystem
	type typeWrapper struct {
		typeAlias
		Type MessageItemType `json:"type"`
		Role MessageRole     `json:"role"`
	}
	shadow := typeWrapper{
		typeAlias: typeAlias(m),
		Type:      m.MessageItemType(),
		Role:      m.Role(),
	}
	return json.Marshal(shadow)
}

type MessageItemUser struct {
	// The unique ID of the item. This may be provided by the client or generated by the server.
	ID string `json:"id,omitempty"`

	// The content of the message.
	Content []MessageContentInput `json:"content,omitempty"`

	// Identifier for the API object being returned - always realtime.item. Optional when creating a new item.
	Object string `json:"object,omitempty"`

	// The status of the item. Has no effect on the conversation.
	Status ItemStatus `json:"status,omitempty"`
}

func (m MessageItemUser) MessageItemType() MessageItemType {
	return MessageItemTypeMessage
}

func (m MessageItemUser) Role() MessageRole {
	return MessageRoleUser
}

func (m MessageItemUser) MarshalJSON() ([]byte, error) {
	type typeAlias MessageItemUser
	type typeWrapper struct {
		typeAlias
		Type MessageItemType `json:"type"`
		Role MessageRole     `json:"role"`
	}
	shadow := typeWrapper{
		typeAlias: typeAlias(m),
		Type:      m.MessageItemType(),
		Role:      m.Role(),
	}
	return json.Marshal(shadow)
}

type MessageItemAssistant struct {
	// The unique ID of the item. This may be provided by the client or generated by the server.
	ID string `json:"id,omitempty"`

	// The content of the message.
	Content []MessageContentOutput `json:"content,omitempty"`

	// Identifier for the API object being returned - always realtime.item. Optional when creating a new item.
	Object string `json:"object,omitempty"`

	// The status of the item. Has no effect on the conversation.
	Status ItemStatus `json:"status,omitempty"`
}

func (m MessageItemAssistant) MessageItemType() MessageItemType {
	return MessageItemTypeMessage
}

func (m MessageItemAssistant) Role() MessageRole {
	return MessageRoleAssistant
}

func (m MessageItemAssistant) MarshalJSON() ([]byte, error) {
	type typeAlias MessageItemAssistant
	type typeWrapper struct {
		typeAlias
		Type MessageItemType `json:"type"`
		Role MessageRole     `json:"role"`
	}
	shadow := typeWrapper{
		typeAlias: typeAlias(m),
		Type:      m.MessageItemType(),
		Role:      m.Role(),
	}
	return json.Marshal(shadow)
}

type MessageContentInput struct {
	// The content type (input_text, input_audio, or input_image).
	Type MessageContentType `json:"type"`

	// Base64-encoded audio bytes (for input_audio), these will be parsed as the format specified in the session input audio type configuration. This defaults to PCM 16-bit 24kHz mono if not specified.
	Audio string `json:"audio,omitempty"`

	// The detail level of the image (for input_image). auto will default to high.
	Detail ImageDetail `json:"detail,omitempty"`

	// Base64-encoded image bytes (for input_image) as a data URI. For example data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA.... Supported formats are PNG and JPEG.
	ImageURL string `json:"image_url,omitempty"`

	// The text content (for input_text).
	Text string `json:"text,omitempty"`

	// Transcript of the audio (for input_audio). This is not sent to the model, but will be attached to the message item for reference.
	Transcript string `json:"transcript,omitempty"`
}

type MessageContentOutput struct {
	// The content type (input_text, input_audio, or input_image).
	Type MessageContentType `json:"type,omitempty"`

	// Base64-encoded audio bytes (for input_audio), these will be parsed as the format specified in the session input audio type configuration. This defaults to PCM 16-bit 24kHz mono if not specified.
	Audio string `json:"audio,omitempty"`

	// The text content (for input_text).
	Text string `json:"text,omitempty"`

	// Transcript of the audio (for input_audio). This is not sent to the model, but will be attached to the message item for reference.
	Transcript string `json:"transcript,omitempty"`
}

type MessageItemFunctionCall struct {
	// The unique ID of the item. This may be provided by the client or generated by the server.
	ID string `json:"id,omitempty"`

	// The ID of the function call.
	CallID string `json:"call_id,omitempty"`

	// The arguments of the function call. This is a JSON-encoded string representing the arguments passed to the function, for example {"arg1": "value1", "arg2": 42}.
	Arguments string `json:"arguments,omitempty"`

	// The name of the function being called.
	Name string `json:"name,omitempty"`

	// Identifier for the API object being returned - always realtime.item. Optional when creating a new item.
	Object string `json:"object,omitempty"`

	// The status of the item. Has no effect on the conversation.
	Status ItemStatus `json:"status,omitempty"`
}

func (m MessageItemFunctionCall) MessageItemType() MessageItemType {
	return MessageItemTypeFunctionCall
}

func (m MessageItemFunctionCall) MarshalJSON() ([]byte, error) {
	type typeAlias MessageItemFunctionCall
	type typeWrapper struct {
		typeAlias
		Type MessageItemType `json:"type"`
	}
	shadow := typeWrapper{
		typeAlias: typeAlias(m),
		Type:      m.MessageItemType(),
	}
	return json.Marshal(shadow)
}

type MessageItemFunctionCallOutput struct {
	// The unique ID of the item. This may be provided by the client or generated by the server.
	ID string `json:"id,omitempty"`

	// The ID of the function call this output is for.
	CallID string `json:"call_id,omitempty"`

	// The output of the function call, this is free text and can contain any information or simply be empty.
	Output string `json:"output,omitempty"`

	// Identifier for the API object being returned - always realtime.item. Optional when creating a new item.
	Object string `json:"object,omitempty"`

	// The status of the item. Has no effect on the conversation.
	Status ItemStatus `json:"status,omitempty"`
}

func (m MessageItemFunctionCallOutput) MessageItemType() MessageItemType {
	return MessageItemTypeFunctionCallOutput
}

func (m MessageItemFunctionCallOutput) MarshalJSON() ([]byte, error) {
	type typeAlias MessageItemFunctionCallOutput
	type typeWrapper struct {
		typeAlias
		Type MessageItemType `json:"type"`
	}
	shadow := typeWrapper{
		typeAlias: typeAlias(m),
		Type:      m.MessageItemType(),
	}
	return json.Marshal(shadow)
}

type MessageItemMCPApprovalResponse struct {
	// The unique ID of the approval response.
	ID string `json:"id,omitempty"`

	// The ID of the approval request being answered.
	ApprovalRequestID string `json:"approval_request_id,omitempty"`

	// Whether the request was approved.
	Approve bool `json:"approve,omitempty"`

	// Optional reason for the decision.
	Reason string `json:"reason,omitempty"`
}

func (m MessageItemMCPApprovalResponse) MessageItemType() MessageItemType {
	return MessageItemTypeMCPApprovalResponse
}

func (m MessageItemMCPApprovalResponse) MarshalJSON() ([]byte, error) {
	type typeAlias MessageItemMCPApprovalResponse
	type typeWrapper struct {
		typeAlias
		Type MessageItemType `json:"type"`
	}
	shadow := typeWrapper{
		typeAlias: typeAlias(m),
		Type:      m.MessageItemType(),
	}
	return json.Marshal(shadow)
}

type MCPTool struct {
	// JSON schema describing the tool's expected input shape.
	InputSchema string `json:"input_schema,omitempty"`

	// The name of the MCP tool.
	Name string `json:"name,omitempty"`

	// A human-readable description of what the tool does.
	Description string `json:"description,omitempty"`

	// Additional metadata or annotations supplied by the server.
	Annotations any `json:"annotations,omitempty"`
}

type MessageItemMCPListTools struct {
	// The unique ID of the list.
	ID string `json:"id,omitempty"`

	// The label of the MCP server.
	ServerLabel string `json:"server_label,omitempty"`

	// The tools available on the server.
	Tools []MCPTool `json:"tools,omitempty"`
}

func (m MessageItemMCPListTools) MessageItemType() MessageItemType {
	return MessageItemTypeMCPListTools
}

func (m MessageItemMCPListTools) MarshalJSON() ([]byte, error) {
	type typeAlias MessageItemMCPListTools
	type typeWrapper struct {
		typeAlias
		Type MessageItemType `json:"type"`
	}
	shadow := typeWrapper{
		typeAlias: typeAlias(m),
		Type:      m.MessageItemType(),
	}
	return json.Marshal(shadow)
}

type MCPErrorType string

const (
	MCPErrorTypeProtocolError MCPErrorType = "protocol_error"
	MCPErrorTypeToolExecution MCPErrorType = "tool_execution_error"
	MCPErrorTypeHTTPError     MCPErrorType = "http_error"
)

type MCPProtocolError struct {
	// Numeric error code (protocol-specific).
	Code int `json:"code,omitempty"`

	// Human-readable error message.
	Message string `json:"message,omitempty"`
}

func (m MCPProtocolError) ErrorType() MCPErrorType {
	return MCPErrorTypeProtocolError
}

func (m MCPProtocolError) MarshalJSON() ([]byte, error) {
	type typeAlias MCPProtocolError
	type typeWrapper struct {
		typeAlias
		Type MCPErrorType `json:"type"`
	}
	shadow := typeWrapper{
		typeAlias: typeAlias(m),
		Type:      m.ErrorType(),
	}
	return json.Marshal(shadow)
}

type MCPToolExecutionError struct {
	// Human-readable error message from tool execution.
	Message string `json:"message,omitempty"`
}

func (m MCPToolExecutionError) ErrorType() MCPErrorType {
	return MCPErrorTypeToolExecution
}

func (m MCPToolExecutionError) MarshalJSON() ([]byte, error) {
	type typeAlias MCPToolExecutionError
	type typeWrapper struct {
		typeAlias
		Type MCPErrorType `json:"type"`
	}
	shadow := typeWrapper{
		typeAlias: typeAlias(m),
		Type:      m.ErrorType(),
	}
	return json.Marshal(shadow)
}

type MCPHTTPError struct {
	// HTTP status code returned by the upstream call.
	Code int `json:"code,omitempty"`

	// Human-readable HTTP error message.
	Message string `json:"message,omitempty"`
}

func (m MCPHTTPError) ErrorType() MCPErrorType {
	return MCPErrorTypeHTTPError
}

func (m MCPHTTPError) MarshalJSON() ([]byte, error) {
	type typeAlias MCPHTTPError
	type typeWrapper struct {
		typeAlias
		Type MCPErrorType `json:"type"`
	}
	shadow := typeWrapper{
		typeAlias: typeAlias(m),
		Type:      m.ErrorType(),
	}
	return json.Marshal(shadow)
}

type MCPError struct {
	// Details when type is protocol_error.
	Protocol *MCPProtocolError `json:",omitempty"`

	// Details when type is tool_execution_error.
	ToolExecution *MCPToolExecutionError `json:",omitempty"`

	// Details when type is http_error.
	HTTP *MCPHTTPError `json:",omitempty"`
}

func (m MCPError) MarshalJSON() ([]byte, error) {
	if m.Protocol != nil {
		return json.Marshal(m.Protocol)
	}
	if m.ToolExecution != nil {
		return json.Marshal(m.ToolExecution)
	}
	return json.Marshal(m.HTTP)
}

func (m *MCPError) UnmarshalJSON(data []byte) error {
	if isNull(data) {
		return nil
	}
	var u typeStruct
	if err := json.Unmarshal(data, &u); err != nil {
		return err
	}
	switch MCPErrorType(u.Type) {
	case MCPErrorTypeProtocolError:
		return json.Unmarshal(data, &m.Protocol)
	case MCPErrorTypeToolExecution:
		return json.Unmarshal(data, &m.ToolExecution)
	case MCPErrorTypeHTTPError:
		return json.Unmarshal(data, &m.HTTP)
	default:
		return errors.New("unknown error type: " + u.Type)
	}
}

type MessageItemMCPToolCall struct {
	// The unique ID of the tool call.
	ID string `json:"id,omitempty"`

	// The label of the MCP server running the tool.
	ServerLabel string `json:"server_label,omitempty"`

	// A JSON string of the arguments passed to the tool.
	Arguments string `json:"arguments,omitempty"`

	// The name of the tool that was run.
	Name string `json:"name,omitempty"`

	// The ID of an associated approval request, if any.
	ApprovalRequestID string `json:"approval_request_id,omitempty"`

	// The error from the tool call, if any.
	Error *MCPProtocolError `json:"error,omitempty"`

	// The output from the tool call.
	Output string `json:"output,omitempty"`
}

func (m MessageItemMCPToolCall) MessageItemType() MessageItemType {
	return MessageItemTypeMCPCall
}

func (m MessageItemMCPToolCall) MarshalJSON() ([]byte, error) {
	type typeAlias MessageItemMCPToolCall
	type typeWrapper struct {
		typeAlias
		Type MessageItemType `json:"type"`
	}
	shadow := typeWrapper{
		typeAlias: typeAlias(m),
		Type:      m.MessageItemType(),
	}
	return json.Marshal(shadow)
}

type MessageItemMCPApprovalRequest struct {
	// The unique ID of the approval request.
	ID string `json:"id,omitempty"`

	// The name of the tool to run.
	Name string `json:"name,omitempty"`

	// A JSON string of arguments for the tool.
	Arguments string `json:"arguments,omitempty"`

	// The label of the MCP server making the request.
	ServerLabel string `json:"server_label,omitempty"`
}

func (m MessageItemMCPApprovalRequest) MessageItemType() MessageItemType {
	return MessageItemTypeMCPApprovalRequest
}

func (m MessageItemMCPApprovalRequest) MarshalJSON() ([]byte, error) {
	type typeAlias MessageItemMCPApprovalRequest
	type typeWrapper struct {
		typeAlias
		Type MessageItemType `json:"type"`
	}
	shadow := typeWrapper{
		typeAlias: typeAlias(m),
		Type:      m.MessageItemType(),
	}
	return json.Marshal(shadow)
}

type MessageItemUnion struct {
	// A system message in a Realtime conversation can be used to provide additional context or instructions to the model. This is similar but distinct from the instruction prompt provided at the start of a conversation, as system messages can be added at any point in the conversation. For major changes to the conversation's behavior, use instructions, but for smaller updates (e.g. "the user is now asking about a different topic"), use system messages.
	System *MessageItemSystem `json:",omitempty"`

	// A user message item in a Realtime conversation.
	User *MessageItemUser `json:",omitempty"`

	// An assistant message item in a Realtime conversation.
	Assistant *MessageItemAssistant `json:",omitempty"`

	// A function call item in a Realtime conversation.
	FunctionCall *MessageItemFunctionCall `json:",omitempty"`

	// A function call output item in a Realtime conversation.
	FunctionCallOutput *MessageItemFunctionCallOutput `json:",omitempty"`

	// A Realtime item responding to an MCP approval request.
	MCPApprovalResponse *MessageItemMCPApprovalResponse `json:",omitempty"`

	// A Realtime item listing tools available on an MCP server.
	MCPListTools *MessageItemMCPListTools `json:",omitempty"`

	// A Realtime item representing an invocation of a tool on an MCP server.
	MCPToolCall *MessageItemMCPToolCall `json:",omitempty"`

	// A Realtime item requesting human approval of a tool invocation.
	MCPApprovalRequest *MessageItemMCPApprovalRequest `json:",omitempty"`
}

func (m MessageItemUnion) MarshalJSON() ([]byte, error) {
	switch {
	case m.System != nil:
		return json.Marshal(m.System)
	case m.User != nil:
		return json.Marshal(m.User)
	case m.Assistant != nil:
		return json.Marshal(m.Assistant)
	case m.FunctionCall != nil:
		return json.Marshal(m.FunctionCall)
	case m.FunctionCallOutput != nil:
		return json.Marshal(m.FunctionCallOutput)
	case m.MCPApprovalResponse != nil:
		return json.Marshal(m.MCPApprovalResponse)
	case m.MCPListTools != nil:
		return json.Marshal(m.MCPListTools)
	case m.MCPToolCall != nil:
		return json.Marshal(m.MCPToolCall)
	case m.MCPApprovalRequest != nil:
		return json.Marshal(m.MCPApprovalRequest)
	default:
		return nil, errors.New("unknown message item type")
	}
}

func (m *MessageItemUnion) UnmarshalJSON(data []byte) error {
	if isNull(data) {
		return nil
	}
	var t struct {
		Type string `json:"type"`
		Role string `json:"role"`
	}
	if err := json.Unmarshal(data, &t); err != nil {
		return err
	}
	switch MessageItemType(t.Type) {
	case MessageItemTypeMessage:
		switch MessageRole(t.Role) {
		case MessageRoleUser:
			return json.Unmarshal(data, &m.User)
		case MessageRoleAssistant:
			return json.Unmarshal(data, &m.Assistant)
		case MessageRoleSystem:
			return json.Unmarshal(data, &m.System)
		default:
			return fmt.Errorf("unknown message role: %s", t.Role)
		}
	case MessageItemTypeFunctionCall:
		return json.Unmarshal(data, &m.FunctionCall)
	case MessageItemTypeFunctionCallOutput:
		return json.Unmarshal(data, &m.FunctionCallOutput)
	case MessageItemTypeMCPApprovalResponse:
		return json.Unmarshal(data, &m.MCPApprovalResponse)
	case MessageItemTypeMCPListTools:
		return json.Unmarshal(data, &m.MCPListTools)
	case MessageItemTypeMCPCall:
		return json.Unmarshal(data, &m.MCPToolCall)
	case MessageItemTypeMCPApprovalRequest:
		return json.Unmarshal(data, &m.MCPApprovalRequest)
	default:
		return fmt.Errorf("unknown message item type: %s", t.Type)
	}
}
