//! Chat completion models for the Portkey API.
//!
//! This module contains all data models for chat completions, including
//! request and response types following the OpenAI-compatible format.

use serde::{Deserialize, Serialize};
#[cfg(feature = "strum")]
use strum::{Display, EnumString};

/// A chat completion message in a conversation.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "role", rename_all = "lowercase")]
pub enum ChatCompletionRequestMessage {
    /// System message that sets the behavior of the assistant
    System {
        /// The contents of the system message
        content: String,
        /// Optional name for the participant
        #[serde(skip_serializing_if = "Option::is_none")]
        name: Option<String>,
    },
    /// Developer message (new role by OpenAI for select models)
    Developer {
        /// The contents of the developer message
        content: String,
        /// Optional name for the participant
        #[serde(skip_serializing_if = "Option::is_none")]
        name: Option<String>,
    },
    /// User message in the conversation
    User {
        /// The contents of the user message (text or array of content parts)
        content: ChatCompletionUserMessageContent,
        /// Optional name for the participant
        #[serde(skip_serializing_if = "Option::is_none")]
        name: Option<String>,
    },
    /// Assistant message in the conversation
    Assistant {
        /// The contents of the assistant message
        #[serde(skip_serializing_if = "Option::is_none")]
        content: Option<String>,
        /// Optional name for the participant
        #[serde(skip_serializing_if = "Option::is_none")]
        name: Option<String>,
        /// Tool calls generated by the model
        #[serde(skip_serializing_if = "Option::is_none")]
        tool_calls: Option<Vec<ChatCompletionMessageToolCall>>,
        /// Deprecated: Function call (use tool_calls instead)
        #[serde(skip_serializing_if = "Option::is_none")]
        function_call: Option<FunctionCall>,
    },
    /// Tool response message
    Tool {
        /// The contents of the tool message
        content: String,
        /// Tool call ID that this message is responding to
        tool_call_id: String,
    },
    /// Function response message (deprecated)
    Function {
        /// The contents of the function message
        content: String,
        /// The name of the function
        name: String,
    },
}

impl ChatCompletionRequestMessage {
    /// Creates a system message.
    ///
    /// # Example
    ///
    /// ```
    /// use portkey_sdk::model::ChatCompletionRequestMessage;
    ///
    /// let msg = ChatCompletionRequestMessage::system("You are a helpful assistant.");
    /// ```
    pub fn system(content: impl Into<String>) -> Self {
        Self::System {
            content: content.into(),
            name: None,
        }
    }

    /// Creates a user message with text content.
    ///
    /// # Example
    ///
    /// ```
    /// use portkey_sdk::model::ChatCompletionRequestMessage;
    ///
    /// let msg = ChatCompletionRequestMessage::user("Hello!");
    /// ```
    pub fn user(content: impl Into<String>) -> Self {
        Self::User {
            content: ChatCompletionUserMessageContent::Text(content.into()),
            name: None,
        }
    }

    /// Creates an assistant message.
    ///
    /// # Example
    ///
    /// ```
    /// use portkey_sdk::model::ChatCompletionRequestMessage;
    ///
    /// let msg = ChatCompletionRequestMessage::assistant("Hello! How can I help you?");
    /// ```
    pub fn assistant(content: impl Into<String>) -> Self {
        Self::Assistant {
            content: Some(content.into()),
            name: None,
            tool_calls: None,
            function_call: None,
        }
    }
}

/// Content of a user message (can be text or multimodal)
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ChatCompletionUserMessageContent {
    /// Plain text content
    Text(String),
    /// Array of content parts (text and/or images)
    Parts(Vec<ChatCompletionContentPart>),
}

/// A content part in a multimodal message
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ChatCompletionContentPart {
    /// Text content part
    Text {
        /// The text content
        text: String,
    },
    /// Image URL content part
    ImageUrl {
        /// Image URL or base64 encoded image data
        image_url: ImageUrl,
    },
}

/// Image URL configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImageUrl {
    /// URL of the image or base64 encoded image data
    pub url: String,
    /// Detail level of the image (auto, low, or high)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub detail: Option<ImageDetail>,
}

/// Image detail level for vision models
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[cfg_attr(feature = "strum", derive(Display, EnumString))]
#[serde(rename_all = "lowercase")]
#[cfg_attr(feature = "strum", strum(serialize_all = "lowercase"))]
pub enum ImageDetail {
    /// Auto detail level (default)
    #[default]
    Auto,
    /// Low detail level
    Low,
    /// High detail level
    High,
}

/// Function call information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionCall {
    /// The name of the function to call
    pub name: String,
    /// The arguments to call the function with (JSON format)
    pub arguments: String,
}

/// Tool call made by the model
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatCompletionMessageToolCall {
    /// The ID of the tool call
    pub id: String,
    /// The type of tool (currently only "function")
    #[serde(rename = "type")]
    pub tool_type: String,
    /// The function to call
    pub function: FunctionCall,
}

/// Response format configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ResponseFormat {
    /// Text response format (default)
    Text,
    /// JSON object response format
    JsonObject,
    /// JSON schema response format (Structured Outputs)
    JsonSchema {
        /// JSON schema configuration
        json_schema: JsonSchema,
    },
}

/// JSON Schema configuration for Structured Outputs
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonSchema {
    /// A description of what the response format is for
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    /// The name of the response format
    pub name: String,
    /// The JSON Schema object
    pub schema: serde_json::Value,
    /// Whether to enable strict schema adherence
    #[serde(skip_serializing_if = "Option::is_none")]
    pub strict: Option<bool>,
}

/// Stream options for streaming responses
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StreamOptions {
    /// If set, include usage information in the final chunk
    #[serde(skip_serializing_if = "Option::is_none")]
    pub include_usage: Option<bool>,
}

/// Thinking mode configuration for Claude models
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThinkingConfig {
    /// Type of thinking mode (enabled or disabled)
    #[serde(rename = "type")]
    pub thinking_type: ThinkingType,
    /// Maximum number of tokens to allocate for thinking
    #[serde(skip_serializing_if = "Option::is_none")]
    pub budget_tokens: Option<i32>,
}

/// Thinking mode type
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "strum", derive(Display, EnumString))]
#[serde(rename_all = "lowercase")]
#[cfg_attr(feature = "strum", strum(serialize_all = "lowercase"))]
pub enum ThinkingType {
    /// Thinking mode enabled
    Enabled,
    /// Thinking mode disabled
    Disabled,
}

/// Function definition for function calling
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionObject {
    /// The name of the function
    pub name: String,
    /// A description of what the function does
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    /// The parameters the function accepts (JSON Schema)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub parameters: Option<serde_json::Value>,
    /// Whether to enable strict schema adherence
    #[serde(skip_serializing_if = "Option::is_none")]
    pub strict: Option<bool>,
}

/// Tool definition
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Tool {
    /// The type of tool (currently only "function")
    #[serde(rename = "type")]
    pub tool_type: String,
    /// The function definition
    pub function: FunctionObject,
}

/// Tool choice option
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ToolChoice {
    /// Simple string choice (none, auto, required)
    Simple(ToolChoiceSimple),
    /// Named tool choice
    Named(NamedToolChoice),
}

/// Simple tool choice options
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "strum", derive(Display, EnumString))]
#[serde(rename_all = "lowercase")]
#[cfg_attr(feature = "strum", strum(serialize_all = "lowercase"))]
pub enum ToolChoiceSimple {
    /// No tool will be called
    None,
    /// Model can choose to call a tool or not
    Auto,
    /// Model must call one or more tools
    Required,
}

/// Named tool choice to force a specific tool
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NamedToolChoice {
    /// The type of tool
    #[serde(rename = "type")]
    pub tool_type: String,
    /// The function to call
    pub function: NamedFunction,
}

/// Named function for tool choice
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NamedFunction {
    /// The name of the function to call
    pub name: String,
}

/// Chat completion request
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatCompletionRequest {
    /// ID of the model to use
    pub model: String,
    /// A list of messages comprising the conversation
    pub messages: Vec<ChatCompletionRequestMessage>,
    /// Number between -2.0 and 2.0 for frequency penalty
    #[serde(skip_serializing_if = "Option::is_none")]
    pub frequency_penalty: Option<f32>,
    /// Modify the likelihood of specified tokens
    #[serde(skip_serializing_if = "Option::is_none")]
    pub logit_bias: Option<serde_json::Map<String, serde_json::Value>>,
    /// Whether to return log probabilities
    #[serde(skip_serializing_if = "Option::is_none")]
    pub logprobs: Option<bool>,
    /// Number of most likely tokens to return at each position
    #[serde(skip_serializing_if = "Option::is_none")]
    pub top_logprobs: Option<i32>,
    /// Maximum number of tokens to generate
    #[serde(skip_serializing_if = "Option::is_none")]
    pub max_tokens: Option<i32>,
    /// How many chat completion choices to generate
    #[serde(skip_serializing_if = "Option::is_none")]
    pub n: Option<i32>,
    /// Number between -2.0 and 2.0 for presence penalty
    #[serde(skip_serializing_if = "Option::is_none")]
    pub presence_penalty: Option<f32>,
    /// Response format configuration
    #[serde(skip_serializing_if = "Option::is_none")]
    pub response_format: Option<ResponseFormat>,
    /// Seed for deterministic sampling
    #[serde(skip_serializing_if = "Option::is_none")]
    pub seed: Option<i64>,
    /// Stop sequences
    #[serde(skip_serializing_if = "Option::is_none")]
    pub stop: Option<Stop>,
    /// Whether to stream the response
    #[serde(skip_serializing_if = "Option::is_none")]
    pub stream: Option<bool>,
    /// Stream options
    #[serde(skip_serializing_if = "Option::is_none")]
    pub stream_options: Option<StreamOptions>,
    /// Thinking mode configuration (Claude models)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub thinking: Option<ThinkingConfig>,
    /// Sampling temperature (0-2)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub temperature: Option<f32>,
    /// Nucleus sampling parameter (0-1)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub top_p: Option<f32>,
    /// List of tools the model may call
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tools: Option<Vec<Tool>>,
    /// Controls which tool is called
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tool_choice: Option<ToolChoice>,
    /// Whether to enable parallel tool calls
    #[serde(skip_serializing_if = "Option::is_none")]
    pub parallel_tool_calls: Option<bool>,
    /// A unique identifier for the end-user
    #[serde(skip_serializing_if = "Option::is_none")]
    pub user: Option<String>,
}

impl ChatCompletionRequest {
    /// Creates a new chat completion request with the minimum required fields.
    ///
    /// # Arguments
    ///
    /// * `model` - The model ID to use (e.g., "gpt-4o", "claude-3-5-sonnet-20241022")
    /// * `messages` - The conversation messages
    ///
    /// # Example
    ///
    /// ```
    /// use portkey_sdk::model::{ChatCompletionRequest, ChatCompletionRequestMessage};
    ///
    /// let request = ChatCompletionRequest::new(
    ///     "gpt-4o",
    ///     vec![
    ///         ChatCompletionRequestMessage::user("Hello!"),
    ///     ],
    /// );
    /// ```
    pub fn new(model: impl Into<String>, messages: Vec<ChatCompletionRequestMessage>) -> Self {
        Self {
            model: model.into(),
            messages,
            frequency_penalty: None,
            logit_bias: None,
            logprobs: None,
            top_logprobs: None,
            max_tokens: None,
            n: None,
            presence_penalty: None,
            response_format: None,
            seed: None,
            stop: None,
            stream: None,
            stream_options: None,
            thinking: None,
            temperature: None,
            top_p: None,
            tools: None,
            tool_choice: None,
            parallel_tool_calls: None,
            user: None,
        }
    }
}

/// Stop sequences (can be a string or array of strings)
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Stop {
    /// Single stop sequence
    Single(String),
    /// Multiple stop sequences
    Multiple(Vec<String>),
}

/// Chat completion response message
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatCompletionResponseMessage {
    /// The role of the message author
    pub role: String,
    /// The contents of the message
    pub content: Option<String>,
    /// Tool calls made by the model
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tool_calls: Option<Vec<ChatCompletionMessageToolCall>>,
    /// Deprecated: Function call (use tool_calls instead)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub function_call: Option<FunctionCall>,
    /// Content blocks (for providers with strict_openai_compliance=false)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub content_blocks: Option<Vec<ContentBlock>>,
}

/// Content block in a response message
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ContentBlock {
    /// Text content block
    Text {
        /// The text content
        text: String,
    },
    /// Thinking content block
    Thinking {
        /// The thinking content
        thinking: String,
    },
    /// Redacted thinking content block
    RedactedThinking {
        /// The redacted thinking data
        data: String,
    },
}

/// Token log probability information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TokenLogprob {
    /// The token
    pub token: String,
    /// Log probability of the token
    pub logprob: f64,
    /// UTF-8 bytes representation of the token
    pub bytes: Option<Vec<i32>>,
    /// Top log probabilities at this position
    pub top_logprobs: Vec<TopLogprob>,
}

/// Top token log probability
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TopLogprob {
    /// The token
    pub token: String,
    /// Log probability of the token
    pub logprob: f64,
    /// UTF-8 bytes representation of the token
    pub bytes: Option<Vec<i32>>,
}

/// Log probability information for a choice
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Logprobs {
    /// List of message content tokens with log probability information
    pub content: Option<Vec<TokenLogprob>>,
}

/// A chat completion choice
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatCompletionChoice {
    /// The reason the model stopped generating tokens
    pub finish_reason: String,
    /// The index of this choice
    pub index: i32,
    /// The completion message
    pub message: ChatCompletionResponseMessage,
    /// Log probability information
    pub logprobs: Option<Logprobs>,
}

/// Token usage statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Usage {
    /// Number of tokens in the prompt
    pub prompt_tokens: i32,
    /// Number of tokens in the completion
    pub completion_tokens: i32,
    /// Total number of tokens used
    pub total_tokens: i32,
}

/// Chat completion response
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatCompletionResponse {
    /// A unique identifier for the chat completion
    pub id: String,
    /// The object type (always "chat.completion")
    pub object: String,
    /// The Unix timestamp when the completion was created
    pub created: i64,
    /// The model used for the chat completion
    pub model: String,
    /// A list of chat completion choices
    pub choices: Vec<ChatCompletionChoice>,
    /// Usage statistics
    #[serde(skip_serializing_if = "Option::is_none")]
    pub usage: Option<Usage>,
    /// System fingerprint
    #[serde(skip_serializing_if = "Option::is_none")]
    pub system_fingerprint: Option<String>,
}
