use core::fmt;
use std::collections::HashMap;

use indexmap::IndexMap;
use serde::{
    de::{self, MapAccess, Visitor},
    Deserialize, Deserializer, Serialize,
};
use serde_json::Value;

// Forked from https://github.com/LlamaEdge/LlamaEdge/blob/6bfe9c12c85bf390c47d6065686caeca700feffa/crates/endpoints/src/chat.rs#L304
/// Represents a chat completion request.
#[derive(Debug, Serialize, Default)]
pub struct ChatCompletionRequest {
    /// The model to use for generating completions.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub model: Option<String>,
    /// A list of messages comprising the conversation so far.
    pub messages: Vec<ChatCompletionRequestMessage>,
    /// Adjust the randomness of the generated text. Between 0.0 and 2.0. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.
    ///
    /// We generally recommend altering this or top_p but not both.
    /// Defaults to 1.0.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub temperature: Option<f64>,
    /// Limit the next token selection to a subset of tokens with a cumulative probability above a threshold P. The value should be between 0.0 and 1.0.
    ///
    /// Top-p sampling, also known as nucleus sampling, is another text generation method that selects the next token from a subset of tokens that together have a cumulative probability of at least p. This method provides a balance between diversity and quality by considering both the probabilities of tokens and the number of tokens to sample from. A higher value for top_p (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text.
    ///
    /// We generally recommend altering this or temperature but not both.
    /// Defaults to 1.0.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub top_p: Option<f64>,
    /// How many chat completion choices to generate for each input message.
    /// Defaults to 1.
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename = "n")]
    pub n_choice: Option<u64>,
    /// Whether to stream the results as they are generated. Useful for chatbots.
    /// Defaults to false.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub stream: Option<bool>,
    /// Options for streaming response. Only set this when you set `stream: true`.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub stream_options: Option<StreamOptions>,
    /// A list of tokens at which to stop generation. If None, no stop tokens are used. Up to 4 sequences where the API will stop generating further tokens.
    /// Defaults to None
    #[serde(skip_serializing_if = "Option::is_none")]
    pub stop: Option<Vec<String>>,
    /// The maximum number of tokens to generate. The value should be no less than 1.
    /// Defaults to 1024.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub max_tokens: Option<u64>,
    /// Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.
    /// Defaults to 0.0.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub presence_penalty: Option<f64>,
    /// Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.
    /// Defaults to 0.0.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub frequency_penalty: Option<f64>,
    /// Modify the likelihood of specified tokens appearing in the completion.
    ///
    /// Accepts a json object that maps tokens (specified by their token ID in the tokenizer) to an associated bias value from -100 to 100. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token.
    /// Defaults to None.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub logit_bias: Option<HashMap<String, f64>>,
    /// A unique identifier representing your end-user.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub user: Option<String>,

    //* OpenAI specific parameters
    /// **Deprecated since 0.10.0.** Use `tools` instead.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub functions: Option<Vec<ChatCompletionRequestFunction>>,
    /// **Deprecated since 0.10.0.** Use `tool_choice` instead.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub function_call: Option<String>,

    /// Format that the model must output
    #[serde(skip_serializing_if = "Option::is_none")]
    pub response_format: Option<ChatResponseFormat>,
    /// A list of tools the model may call.
    ///
    /// Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs for.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tools: Option<Vec<Tool>>,
    /// Controls which (if any) function is called by the model.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tool_choice: Option<ToolChoice>,
}
impl<'de> Deserialize<'de> for ChatCompletionRequest {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct ChatCompletionRequestVisitor;

        impl<'de> Visitor<'de> for ChatCompletionRequestVisitor {
            type Value = ChatCompletionRequest;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("struct ChatCompletionRequest")
            }

            fn visit_map<V>(self, mut map: V) -> Result<ChatCompletionRequest, V::Error>
            where
                V: MapAccess<'de>,
            {
                // Initialize all fields as None or empty
                let mut model = None;
                let mut messages = None;
                let mut temperature = None;
                let mut top_p = None;
                let mut n_choice = None;
                let mut stream = None;
                let mut stream_options = None;
                let mut stop = None;
                let mut max_tokens = None;
                let mut presence_penalty = None;
                let mut frequency_penalty = None;
                let mut logit_bias = None;
                let mut user = None;
                let mut functions = None;
                let mut function_call = None;
                let mut response_format = None;
                let mut tools = None;
                let mut tool_choice = None;

                while let Some(key) = map.next_key::<String>()? {
                    match key.as_str() {
                        "model" => model = map.next_value()?,
                        "messages" => messages = map.next_value()?,
                        "temperature" => temperature = map.next_value()?,
                        "top_p" => top_p = map.next_value()?,
                        "n" => n_choice = map.next_value()?,
                        "stream" => stream = map.next_value()?,
                        "stream_options" => stream_options = map.next_value()?,
                        "stop" => stop = map.next_value()?,
                        "max_tokens" => max_tokens = map.next_value()?,
                        "presence_penalty" => presence_penalty = map.next_value()?,
                        "frequency_penalty" => frequency_penalty = map.next_value()?,
                        "logit_bias" => logit_bias = map.next_value()?,
                        "user" => user = map.next_value()?,
                        "functions" => functions = map.next_value()?,
                        "function_call" => function_call = map.next_value()?,
                        "response_format" => response_format = map.next_value()?,
                        "tools" => tools = map.next_value()?,
                        "tool_choice" => tool_choice = map.next_value()?,
                        _ => return Err(de::Error::unknown_field(key.as_str(), FIELDS)),
                    }
                }

                // Ensure all required fields are initialized
                let messages = messages.ok_or_else(|| de::Error::missing_field("messages"))?;

                // Set default value for `max_tokens` if not provided
                if max_tokens.is_none() {
                    max_tokens = Some(1024);
                }

                // Check tools and tool_choice
                // `auto` is the default if tools are present.
                // `none` is the default when no tools are present.
                if tools.is_some() {
                    if tool_choice.is_none() {
                        tool_choice = Some(ToolChoice::Auto);
                    }
                } else if tool_choice.is_none() {
                    tool_choice = Some(ToolChoice::None);
                }

                if n_choice.is_none() {
                    n_choice = Some(1);
                }

                if stream.is_none() {
                    stream = Some(false);
                }

                // Construct ChatCompletionRequest with all fields
                Ok(ChatCompletionRequest {
                    model,
                    messages,
                    temperature,
                    top_p,
                    n_choice,
                    stream,
                    stream_options,
                    stop,
                    max_tokens,
                    presence_penalty,
                    frequency_penalty,
                    logit_bias,
                    user,
                    functions,
                    function_call,
                    response_format,
                    tools,
                    tool_choice,
                })
            }
        }

        const FIELDS: &[&str] = &[
            "prompt",
            "max_tokens",
            "temperature",
            "top_p",
            "n",
            "stream",
            "stream_options",
            "stop",
            "max_tokens",
            "presence_penalty",
            "frequency_penalty",
            "logit_bias",
            "user",
            "functions",
            "function_call",
            "response_format",
            "tools",
            "tool_choice",
        ];
        deserializer.deserialize_struct(
            "ChatCompletionRequest",
            FIELDS,
            ChatCompletionRequestVisitor,
        )
    }
}

/// Message for comprising the conversation.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(tag = "role", rename_all = "lowercase")]
pub enum ChatCompletionRequestMessage {
    System(ChatCompletionSystemMessage),
    User(ChatCompletionUserMessage),
    Assistant(ChatCompletionAssistantMessage),
    Tool(ChatCompletionToolMessage),
}
impl ChatCompletionRequestMessage {
    /// Creates a new system message.
    ///
    /// # Arguments
    ///
    /// * `content` - The contents of the system message.
    ///
    /// * `name` - An optional name for the participant. Provides the model information to differentiate between participants of the same role.
    pub fn new_system_message(content: impl Into<String>, name: Option<String>) -> Self {
        ChatCompletionRequestMessage::System(ChatCompletionSystemMessage::new(content, name))
    }

    /// Creates a new user message.
    ///
    /// # Arguments
    ///
    /// * `content` - The contents of the user message.
    ///
    /// * `name` - An optional name for the participant. Provides the model information to differentiate between participants of the same role.
    pub fn new_user_message(
        content: ChatCompletionUserMessageContent,
        name: Option<String>,
    ) -> Self {
        ChatCompletionRequestMessage::User(ChatCompletionUserMessage::new(content, name))
    }

    /// Creates a new assistant message.
    ///
    /// # Arguments
    ///
    /// * `content` - The contents of the assistant message. Required unless `tool_calls` is specified.
    ///
    /// * `name` - An optional name for the participant. Provides the model information to differentiate between participants of the same role.
    ///
    /// * `tool_calls` - The tool calls generated by the model.
    pub fn new_assistant_message(
        content: Option<String>,
        name: Option<String>,
        tool_calls: Option<Vec<ToolCall>>,
    ) -> Self {
        ChatCompletionRequestMessage::Assistant(ChatCompletionAssistantMessage::new(
            content, name, tool_calls,
        ))
    }

    /// Creates a new tool message.
    pub fn new_tool_message(content: impl Into<String>, tool_call_id: Option<String>) -> Self {
        ChatCompletionRequestMessage::Tool(ChatCompletionToolMessage::new(content, tool_call_id))
    }

    /// The role of the messages author.
    pub fn role(&self) -> ChatCompletionRole {
        match self {
            ChatCompletionRequestMessage::System(_) => ChatCompletionRole::System,
            ChatCompletionRequestMessage::User(_) => ChatCompletionRole::User,
            ChatCompletionRequestMessage::Assistant(_) => ChatCompletionRole::Assistant,
            ChatCompletionRequestMessage::Tool(_) => ChatCompletionRole::Tool,
        }
    }

    /// The name of the participant. Provides the model information to differentiate between participants of the same role.
    pub fn name(&self) -> Option<&String> {
        match self {
            ChatCompletionRequestMessage::System(message) => message.name(),
            ChatCompletionRequestMessage::User(message) => message.name(),
            ChatCompletionRequestMessage::Assistant(message) => message.name(),
            ChatCompletionRequestMessage::Tool(_) => None,
        }
    }
}

/// Sampling methods used for chat completion requests.
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq)]
pub enum ChatCompletionRequestSampling {
    /// What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.
    Temperature(f64),
    /// An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.
    TopP(f64),
}

/// The role of the messages author.
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ChatCompletionRole {
    System,
    User,
    Assistant,
    /// **Deprecated since 0.10.0.** Use [ChatCompletionRole::Tool] instead.
    Function,
    Tool,
}
impl std::fmt::Display for ChatCompletionRole {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ChatCompletionRole::System => write!(f, "system"),
            ChatCompletionRole::User => write!(f, "user"),
            ChatCompletionRole::Assistant => write!(f, "assistant"),
            ChatCompletionRole::Function => write!(f, "function"),
            ChatCompletionRole::Tool => write!(f, "tool"),
        }
    }
}

/// **Deprecated since 0.10.0.** Use [Tool] instead.
#[derive(Debug, Deserialize, Serialize)]
pub struct ChatCompletionRequestFunction {
    name: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    description: Option<String>,
    parameters: ChatCompletionRequestFunctionParameters,
}

/// The parameters the functions accepts, described as a JSON Schema object.
///
/// See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling) for examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format.
///
/// To describe a function that accepts no parameters, provide the value
/// `{"type": "object", "properties": {}}`.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ChatCompletionRequestFunctionParameters {
    #[serde(rename = "type")]
    pub schema_type: JSONSchemaType,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub properties: Option<HashMap<String, Box<JSONSchemaDefine>>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub required: Option<Vec<String>>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum JSONSchemaType {
    Object,
    Number,
    Integer,
    String,
    Array,
    Null,
    Boolean,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JSONSchemaDefine {
    #[serde(rename = "type")]
    pub schema_type: Option<JSONSchemaType>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    #[serde(rename = "enum", skip_serializing_if = "Option::is_none")]
    pub enum_values: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub properties: Option<HashMap<String, Box<JSONSchemaDefine>>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub required: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub items: Option<Box<JSONSchemaDefine>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub default: Option<Value>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub maximum: Option<Value>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub minimum: Option<Value>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub title: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub examples: Option<Vec<Value>>,
}

/// Represents a chat completion response returned by model, based on the provided input.
#[derive(Debug, Deserialize, Serialize)]
pub struct ChatCompletionObject {
    /// A unique identifier for the chat completion.
    pub id: String,
    /// The object type, which is always `chat.completion`.
    pub object: String,
    /// The Unix timestamp (in seconds) of when the chat completion was created.
    pub created: u64,
    /// The model used for the chat completion.
    pub model: String,
    /// A list of chat completion choices. Can be more than one if `n_choice` is greater than 1.
    pub choices: Vec<ChatCompletionObjectChoice>,
    /// Usage statistics for the completion request.
    pub usage: Usage,
}

/// Represents a chat completion choice returned by model.
#[derive(Debug, Deserialize, Serialize)]
pub struct ChatCompletionObjectChoice {
    /// The index of the choice in the list of choices.
    pub index: u32,
    /// A chat completion message generated by the model.
    pub message: ChatCompletionObjectMessage,
    /// The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop sequence, `length` if the maximum number of tokens specified in the request was reached, or `function_call` if the model called a function.
    pub finish_reason: FinishReason,
    /// Log probability information for the choice.
    pub logprobs: Option<LogProbs>,
}

/// Token usage
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct Usage {
    /// Number of tokens in the prompt.
    pub prompt_tokens: u64,
    /// Number of tokens in the generated completion.
    pub completion_tokens: u64,
    /// Total number of tokens used in the request (prompt + completion).
    pub total_tokens: u64,
}

/// The reason the model stopped generating tokens.
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy)]
#[allow(non_camel_case_types)]
pub enum FinishReason {
    /// `stop` if the model hit a natural stop point or a provided stop sequence.
    stop,
    /// `length` if the maximum number of tokens specified in the request was reached.
    length,
    /// `tool_calls` if the model called a tool.
    tool_calls,
}

/// Represents a chat completion message generated by the model.
#[derive(Debug, Serialize)]
pub struct ChatCompletionObjectMessage {
    /// The contents of the message.
    pub content: Option<String>,
    /// The tool calls generated by the model, such as function calls.
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub tool_calls: Vec<ToolCall>,
    /// The role of the author of this message.
    pub role: ChatCompletionRole,
    /// Deprecated. The name and arguments of a function that should be called, as generated by the model.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub function_call: Option<ChatMessageFunctionCall>,
}
impl<'de> Deserialize<'de> for ChatCompletionObjectMessage {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct ChatCompletionObjectMessageVisitor;

        impl<'de> Visitor<'de> for ChatCompletionObjectMessageVisitor {
            type Value = ChatCompletionObjectMessage;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("struct ChatCompletionObjectMessage")
            }

            fn visit_map<V>(self, mut map: V) -> Result<ChatCompletionObjectMessage, V::Error>
            where
                V: MapAccess<'de>,
            {
                let mut content = None;
                let mut tool_calls = None;
                let mut role = None;
                let mut function_call = None;

                while let Some(key) = map.next_key::<String>()? {
                    match key.as_str() {
                        "content" => content = map.next_value()?,
                        "tool_calls" => tool_calls = map.next_value()?,
                        "role" => role = map.next_value()?,
                        "function_call" => function_call = map.next_value()?,
                        _ => return Err(de::Error::unknown_field(key.as_str(), FIELDS)),
                    }
                }

                let content = content;
                let tool_calls = tool_calls.unwrap_or_default();
                let role = role.ok_or_else(|| de::Error::missing_field("role"))?;
                let function_call = function_call;

                Ok(ChatCompletionObjectMessage {
                    content,
                    tool_calls,
                    role,
                    function_call,
                })
            }
        }

        const FIELDS: &[&str] = &["content", "tool_calls", "role", "function_call"];
        deserializer.deserialize_struct(
            "ChatCompletionObjectMessage",
            FIELDS,
            ChatCompletionObjectMessageVisitor,
        )
    }
}

/// Options for streaming response. Only set this when you set stream: `true``.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct StreamOptions {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub include_usage: Option<bool>,
}

/// The name and arguments of a function that should be called, as generated by the model.
#[derive(Debug, Deserialize, Serialize)]
pub struct ChatMessageFunctionCall {
    /// The name of the function to call.
    pub name: String,

    /// The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.
    pub arguments: String,
}

/// Represents a tool call generated by the model.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct ToolCall {
    /// The ID of the tool call.
    pub id: String,
    /// The type of the tool. Currently, only function is supported.
    #[serde(rename = "type")]
    pub ty: String,
    /// The function that the model called.
    pub function: Function,
}

/// Log probability information for the choice.
#[derive(Debug, Deserialize, Serialize)]
pub struct LogProbs;

/// The function that the model called.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct Function {
    /// The name of the function that the model called.
    pub name: String,
    /// The arguments that the model called the function with.
    pub arguments: String,
}

/// Defines the types of a user message content.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum ChatCompletionUserMessageContent {
    /// The text contents of the message.
    Text(String),
    /// An array of content parts with a defined type, each can be of type `text` or `image_url` when passing in images.
    /// It is required that there must be one content part of type `text` at least. Multiple images are allowed by adding multiple image_url content parts.
    Parts(Vec<ContentPart>),
}
impl ChatCompletionUserMessageContent {
    pub fn ty(&self) -> &str {
        match self {
            ChatCompletionUserMessageContent::Text(_) => "text",
            ChatCompletionUserMessageContent::Parts(_) => "parts",
        }
    }
}

/// Define the content part of a user message.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(tag = "type", rename_all = "lowercase")]
// #[serde(untagged)]
pub enum ContentPart {
    #[serde(rename = "text")]
    Text(TextContentPart),
    #[serde(rename = "image_url")]
    Image(ImageContentPart),
}
impl ContentPart {
    pub fn ty(&self) -> &str {
        match self {
            ContentPart::Text(_) => "text",
            ContentPart::Image(_) => "image_url",
        }
    }
}

/// Represents the text part of a user message content.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct TextContentPart {
    /// The text content.
    text: String,
}
impl TextContentPart {
    pub fn new(text: impl Into<String>) -> Self {
        Self { text: text.into() }
    }

    /// The text content.
    pub fn text(&self) -> &str {
        &self.text
    }
}

/// Represents the image part of a user message content.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct ImageContentPart {
    #[serde(rename = "image_url")]
    image: Image,
}
impl ImageContentPart {
    pub fn new(image: Image) -> Self {
        Self { image }
    }

    /// The image URL.
    pub fn image(&self) -> &Image {
        &self.image
    }
}

/// JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib)
/// PNG 1/2/4/8/16-bit-per-channel
///
/// TGA (not sure what subset, if a subset)
/// BMP non-1bpp, non-RLE
/// PSD (composited view only, no extra channels, 8/16 bit-per-channel)
///
/// GIF (*comp always reports as 4-channel)
/// HDR (radiance rgbE format)
/// PIC (Softimage PIC)
/// PNM (PPM and PGM binary only)
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct Image {
    /// Either a URL of the image or the base64 encoded image data.
    pub url: String,
    /// Specifies the detail level of the image. Defaults to auto.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub detail: Option<String>,
}
impl Image {
    pub fn is_url(&self) -> bool {
        url::Url::parse(&self.url).is_ok()
    }
}

/// Defines the content of a tool message.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct ChatCompletionToolMessage {
    /// The contents of the tool message.
    content: String,
    /// Tool call that this message is responding to.
    #[serde(skip_serializing_if = "Option::is_none")]
    tool_call_id: Option<String>,
}
impl ChatCompletionToolMessage {
    /// Creates a new tool message.
    ///
    /// # Arguments
    ///
    /// * `content` - The contents of the tool message.
    ///
    /// * `tool_call_id` - Tool call that this message is responding to.
    pub fn new(content: impl Into<String>, tool_call_id: Option<String>) -> Self {
        Self {
            content: content.into(),
            tool_call_id,
        }
    }

    /// The role of the messages author, in this case `tool`.
    pub fn role(&self) -> ChatCompletionRole {
        ChatCompletionRole::Tool
    }

    /// The contents of the tool message.
    pub fn content(&self) -> &str {
        &self.content
    }

    /// Tool call that this message is responding to.
    pub fn tool_call_id(&self) -> Option<String> {
        self.tool_call_id.clone()
    }
}

/// Defines the content of an assistant message.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct ChatCompletionAssistantMessage {
    /// The contents of the assistant message. Required unless `tool_calls` is specified.
    #[serde(skip_serializing_if = "Option::is_none")]
    content: Option<String>,
    /// An optional name for the participant. Provides the model information to differentiate between participants of the same role.
    #[serde(skip_serializing_if = "Option::is_none")]
    name: Option<String>,
    /// The tool calls generated by the model.
    #[serde(skip_serializing_if = "Option::is_none")]
    tool_calls: Option<Vec<ToolCall>>,
}
impl ChatCompletionAssistantMessage {
    /// Creates a new assistant message.
    ///
    /// # Arguments
    ///
    /// * `content` - The contents of the assistant message. Required unless `tool_calls` is specified.
    ///
    /// * `name` - An optional name for the participant. Provides the model information to differentiate between participants of the same role.
    ///
    /// * `tool_calls` - The tool calls generated by the model.
    pub fn new(
        content: Option<String>,
        name: Option<String>,
        tool_calls: Option<Vec<ToolCall>>,
    ) -> Self {
        match tool_calls.is_some() {
            true => Self {
                content: None,
                name,
                tool_calls,
            },
            false => Self {
                content,
                name,
                tool_calls: None,
            },
        }
    }

    /// The role of the messages author, in this case `assistant`.
    pub fn role(&self) -> ChatCompletionRole {
        ChatCompletionRole::Assistant
    }

    /// The contents of the assistant message. If `tool_calls` is specified, then `content` is None.
    pub fn content(&self) -> Option<&String> {
        self.content.as_ref()
    }

    /// An optional name for the participant.
    pub fn name(&self) -> Option<&String> {
        self.name.as_ref()
    }

    /// The tool calls generated by the model.
    pub fn tool_calls(&self) -> Option<&Vec<ToolCall>> {
        self.tool_calls.as_ref()
    }
}

/// Defines the content of a system message.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct ChatCompletionSystemMessage {
    /// The contents of the system message.
    content: String,
    /// An optional name for the participant. Provides the model information to differentiate between participants of the same role.
    #[serde(skip_serializing_if = "Option::is_none")]
    name: Option<String>,
}
impl ChatCompletionSystemMessage {
    /// Creates a new system message.
    ///
    /// # Arguments
    ///
    /// * `content` - The contents of the system message.
    ///
    /// * `name` - An optional name for the participant. Provides the model information to differentiate between participants of the same role.
    pub fn new(content: impl Into<String>, name: Option<String>) -> Self {
        Self {
            content: content.into(),
            name,
        }
    }

    pub fn role(&self) -> ChatCompletionRole {
        ChatCompletionRole::System
    }

    pub fn content(&self) -> &str {
        &self.content
    }

    pub fn name(&self) -> Option<&String> {
        self.name.as_ref()
    }
}

/// Defines the content of a user message.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct ChatCompletionUserMessage {
    /// The contents of the user message.
    content: ChatCompletionUserMessageContent,
    /// An optional name for the participant. Provides the model information to differentiate between participants of the same role.
    #[serde(skip_serializing_if = "Option::is_none")]
    name: Option<String>,
}
impl ChatCompletionUserMessage {
    /// Creates a new user message.
    ///
    /// # Arguments
    ///
    /// * `content` - The contents of the user message.
    ///
    /// * `name` - An optional name for the participant. Provides the model information to differentiate between participants of the same role.
    pub fn new(content: ChatCompletionUserMessageContent, name: Option<String>) -> Self {
        Self { content, name }
    }

    pub fn role(&self) -> ChatCompletionRole {
        ChatCompletionRole::User
    }

    pub fn content(&self) -> &ChatCompletionUserMessageContent {
        &self.content
    }

    pub fn name(&self) -> Option<&String> {
        self.name.as_ref()
    }
}

/// An object specifying the format that the model must output.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ChatResponseFormat {
    /// Must be one of `text`` or `json_object`. Defaults to `text`.
    #[serde(rename = "type")]
    pub ty: String,
}
impl Default for ChatResponseFormat {
    fn default() -> Self {
        Self {
            ty: "text".to_string(),
        }
    }
}

/// Controls which (if any) function is called by the model. Defaults to `None`.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub enum ToolChoice {
    /// The model will not call a function and instead generates a message.
    #[serde(rename = "none")]
    None,
    /// The model can pick between generating a message or calling a function.
    #[serde(rename = "auto")]
    Auto,
    /// The model must call one or more tools.
    #[serde(rename = "required")]
    Required,
    /// Specifies a tool the model should use. Use to force the model to call a specific function.
    #[serde(untagged)]
    Tool(ToolChoiceTool),
}
impl Default for ToolChoice {
    fn default() -> Self {
        Self::None
    }
}

/// A tool the model should use.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct ToolChoiceTool {
    /// The type of the tool. Currently, only `function` is supported.
    #[serde(rename = "type")]
    pub ty: String,
    /// The function the model calls.
    pub function: ToolChoiceToolFunction,
}

/// Represents a tool the model should use.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct ToolChoiceToolFunction {
    /// The name of the function to call.
    pub name: String,
}

/// Represents a tool the model may generate JSON inputs for.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Tool {
    /// The type of the tool. Currently, only `function` is supported.
    #[serde(rename = "type")]
    pub ty: String,
    /// Function the model may generate JSON inputs for.
    pub function: ToolFunction,
}

/// Function the model may generate JSON inputs for.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ToolFunction {
    /// The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.
    pub name: String,
    /// A description of what the function does, used by the model to choose when and how to call the function.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    // The parameters the functions accepts, described as a JSON Schema object.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub parameters: Option<ToolFunctionParameters>,
}

/// The parameters the functions accepts, described as a JSON Schema object.
///
/// See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling) for examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format.
///
/// To describe a function that accepts no parameters, provide the value
/// `{"type": "object", "properties": {}}`.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ToolFunctionParameters {
    #[serde(rename = "type")]
    pub schema_type: JSONSchemaType,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub properties: Option<IndexMap<String, Box<JSONSchemaDefine>>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub required: Option<Vec<String>>,
}
