//! ResponseResource type for the OpenResponses API.

use serde::{Deserialize, Serialize};
use serde_json::Value;
use utoipa::ToSchema;

use mistralrs_core::{Tool, ToolChoice};

use super::{
    enums::{IncompleteReason, ResponseStatus, TruncationStrategy},
    items::OutputItem,
};

use crate::responses::TextConfig;

/// Usage information for a response
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, Default)]
pub struct ResponseUsage {
    /// Number of input tokens
    pub input_tokens: usize,
    /// Number of output tokens
    pub output_tokens: usize,
    /// Total number of tokens
    pub total_tokens: usize,
    /// Detailed input token breakdown
    #[serde(skip_serializing_if = "Option::is_none")]
    pub input_tokens_details: Option<InputTokensDetails>,
    /// Detailed output token breakdown
    #[serde(skip_serializing_if = "Option::is_none")]
    pub output_tokens_details: Option<OutputTokensDetails>,
}

impl ResponseUsage {
    /// Create a new ResponseUsage
    pub fn new(input_tokens: usize, output_tokens: usize) -> Self {
        Self {
            input_tokens,
            output_tokens,
            total_tokens: input_tokens + output_tokens,
            input_tokens_details: None,
            output_tokens_details: None,
        }
    }
}

/// Detailed input token breakdown
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, Default)]
pub struct InputTokensDetails {
    /// Audio tokens
    #[serde(skip_serializing_if = "Option::is_none")]
    pub audio_tokens: Option<usize>,
    /// Cached tokens
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cached_tokens: Option<usize>,
    /// Image tokens
    #[serde(skip_serializing_if = "Option::is_none")]
    pub image_tokens: Option<usize>,
    /// Text tokens
    #[serde(skip_serializing_if = "Option::is_none")]
    pub text_tokens: Option<usize>,
}

/// Detailed output token breakdown
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, Default)]
pub struct OutputTokensDetails {
    /// Audio tokens
    #[serde(skip_serializing_if = "Option::is_none")]
    pub audio_tokens: Option<usize>,
    /// Text tokens
    #[serde(skip_serializing_if = "Option::is_none")]
    pub text_tokens: Option<usize>,
    /// Reasoning tokens
    #[serde(skip_serializing_if = "Option::is_none")]
    pub reasoning_tokens: Option<usize>,
}

/// Error information for a response
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ResponseError {
    /// Error type
    #[serde(rename = "type")]
    pub error_type: String,
    /// Error code
    #[serde(skip_serializing_if = "Option::is_none")]
    pub code: Option<String>,
    /// Error message
    pub message: String,
}

impl ResponseError {
    /// Create a new ResponseError
    pub fn new(error_type: impl Into<String>, message: impl Into<String>) -> Self {
        Self {
            error_type: error_type.into(),
            code: None,
            message: message.into(),
        }
    }

    /// Create a new ResponseError with a code
    pub fn with_code(
        error_type: impl Into<String>,
        code: impl Into<String>,
        message: impl Into<String>,
    ) -> Self {
        Self {
            error_type: error_type.into(),
            code: Some(code.into()),
            message: message.into(),
        }
    }
}

/// Details about incomplete responses
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct IncompleteDetails {
    /// Reason for incompleteness
    pub reason: IncompleteReason,
}

impl IncompleteDetails {
    /// Create new incomplete details
    pub fn new(reason: IncompleteReason) -> Self {
        Self { reason }
    }

    /// Create incomplete details for max tokens reached
    pub fn max_output_tokens() -> Self {
        Self::new(IncompleteReason::MaxOutputTokens)
    }

    /// Create incomplete details for content filter
    pub fn content_filter() -> Self {
        Self::new(IncompleteReason::ContentFilter)
    }
}

/// The main response resource returned by the OpenResponses API
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ResponseResource {
    /// Unique identifier for this response
    pub id: String,
    /// Object type (always "response")
    pub object: &'static str,
    /// Unix timestamp when the response was created
    pub created_at: u64,
    /// Unix timestamp when the response was completed (if completed)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub completed_at: Option<u64>,
    /// The model used for this response
    pub model: String,
    /// Current status of the response
    pub status: ResponseStatus,
    /// Output items generated by the model
    pub output: Vec<OutputItem>,
    /// Concatenated text from all text output content
    #[serde(skip_serializing_if = "Option::is_none")]
    pub output_text: Option<String>,
    /// Reasoning/chain-of-thought content from the model
    #[serde(skip_serializing_if = "Option::is_none")]
    pub reasoning: Option<String>,
    /// Token usage information
    #[serde(skip_serializing_if = "Option::is_none")]
    pub usage: Option<ResponseUsage>,
    /// Error information (if status is failed)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub error: Option<ResponseError>,
    /// Details about why response is incomplete
    #[serde(skip_serializing_if = "Option::is_none")]
    pub incomplete_details: Option<IncompleteDetails>,
    /// User-provided metadata
    #[serde(skip_serializing_if = "Option::is_none")]
    pub metadata: Option<Value>,
    /// Truncation strategy used
    #[serde(skip_serializing_if = "Option::is_none")]
    pub truncation: Option<TruncationStrategy>,
    /// System instructions (if provided)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub instructions: Option<String>,
    /// Previous response ID (for multi-turn conversations)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub previous_response_id: Option<String>,

    // ===== Request Parameters (echoed back) =====
    /// Tool definitions from the request
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tools: Option<Vec<Tool>>,
    /// Tool choice configuration from the request
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tool_choice: Option<ToolChoice>,
    /// Whether parallel tool calls are enabled
    #[serde(skip_serializing_if = "Option::is_none")]
    pub parallel_tool_calls: Option<bool>,
    /// Text configuration from the request
    #[serde(skip_serializing_if = "Option::is_none")]
    pub text: Option<TextConfig>,
    /// Temperature from the request
    #[serde(skip_serializing_if = "Option::is_none")]
    pub temperature: Option<f64>,
    /// Top-p from the request
    #[serde(skip_serializing_if = "Option::is_none")]
    pub top_p: Option<f64>,
    /// Presence penalty from the request
    #[serde(skip_serializing_if = "Option::is_none")]
    pub presence_penalty: Option<f32>,
    /// Frequency penalty from the request
    #[serde(skip_serializing_if = "Option::is_none")]
    pub frequency_penalty: Option<f32>,
    /// Top logprobs from the request
    #[serde(skip_serializing_if = "Option::is_none")]
    pub top_logprobs: Option<usize>,
    /// Max output tokens from the request
    #[serde(skip_serializing_if = "Option::is_none")]
    pub max_output_tokens: Option<usize>,
    /// Max tool calls from the request (even if unsupported)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub max_tool_calls: Option<usize>,
    /// Whether to store the response
    #[serde(skip_serializing_if = "Option::is_none")]
    pub store: Option<bool>,
    /// Whether request runs in background
    #[serde(skip_serializing_if = "Option::is_none")]
    pub background: Option<bool>,
}

impl ResponseResource {
    /// Create a new ResponseResource with default values
    pub fn new(id: String, model: String, created_at: u64) -> Self {
        Self {
            id,
            object: "response",
            created_at,
            completed_at: None,
            model,
            status: ResponseStatus::Queued,
            output: Vec::new(),
            output_text: None,
            reasoning: None,
            usage: None,
            error: None,
            incomplete_details: None,
            metadata: None,
            truncation: None,
            instructions: None,
            previous_response_id: None,
            // Request parameters (echoed back)
            tools: None,
            tool_choice: None,
            parallel_tool_calls: None,
            text: None,
            temperature: None,
            top_p: None,
            presence_penalty: None,
            frequency_penalty: None,
            top_logprobs: None,
            max_output_tokens: None,
            max_tool_calls: None,
            store: None,
            background: None,
        }
    }

    /// Set the status
    pub fn with_status(mut self, status: ResponseStatus) -> Self {
        self.status = status;
        self
    }

    /// Set the output items
    pub fn with_output(mut self, output: Vec<OutputItem>) -> Self {
        self.output = output;
        self
    }

    /// Set the output text
    pub fn with_output_text(mut self, output_text: Option<String>) -> Self {
        self.output_text = output_text;
        self
    }

    /// Set the reasoning content
    pub fn with_reasoning(mut self, reasoning: Option<String>) -> Self {
        self.reasoning = reasoning;
        self
    }

    /// Set the usage information
    pub fn with_usage(mut self, usage: ResponseUsage) -> Self {
        self.usage = Some(usage);
        self
    }

    /// Set the error information
    pub fn with_error(mut self, error: ResponseError) -> Self {
        self.error = Some(error);
        self.status = ResponseStatus::Failed;
        self
    }

    /// Set the metadata
    pub fn with_metadata(mut self, metadata: Value) -> Self {
        self.metadata = Some(metadata);
        self
    }

    /// Set the completion timestamp
    pub fn with_completed_at(mut self, completed_at: u64) -> Self {
        self.completed_at = Some(completed_at);
        self
    }

    /// Set incomplete details
    pub fn with_incomplete_details(mut self, details: IncompleteDetails) -> Self {
        self.incomplete_details = Some(details);
        self.status = ResponseStatus::Incomplete;
        self
    }

    /// Mark the response as in progress
    pub fn in_progress(&mut self) {
        self.status = ResponseStatus::InProgress;
    }

    /// Mark the response as completed
    pub fn complete(&mut self, completed_at: u64) {
        self.status = ResponseStatus::Completed;
        self.completed_at = Some(completed_at);
    }

    /// Mark the response as failed
    pub fn fail(&mut self, error: ResponseError) {
        self.status = ResponseStatus::Failed;
        self.error = Some(error);
    }

    /// Mark the response as cancelled
    pub fn cancel(&mut self) {
        self.status = ResponseStatus::Cancelled;
    }

    // ===== Builder methods for request parameters =====

    /// Set the tools
    pub fn with_tools(mut self, tools: Option<Vec<Tool>>) -> Self {
        self.tools = tools;
        self
    }

    /// Set the tool choice
    pub fn with_tool_choice(mut self, tool_choice: Option<ToolChoice>) -> Self {
        self.tool_choice = tool_choice;
        self
    }

    /// Set parallel tool calls
    pub fn with_parallel_tool_calls(mut self, parallel_tool_calls: Option<bool>) -> Self {
        self.parallel_tool_calls = parallel_tool_calls;
        self
    }

    /// Set text config
    pub fn with_text(mut self, text: Option<TextConfig>) -> Self {
        self.text = text;
        self
    }

    /// Set temperature
    pub fn with_temperature(mut self, temperature: Option<f64>) -> Self {
        self.temperature = temperature;
        self
    }

    /// Set top_p
    pub fn with_top_p(mut self, top_p: Option<f64>) -> Self {
        self.top_p = top_p;
        self
    }

    /// Set presence penalty
    pub fn with_presence_penalty(mut self, presence_penalty: Option<f32>) -> Self {
        self.presence_penalty = presence_penalty;
        self
    }

    /// Set frequency penalty
    pub fn with_frequency_penalty(mut self, frequency_penalty: Option<f32>) -> Self {
        self.frequency_penalty = frequency_penalty;
        self
    }

    /// Set top logprobs
    pub fn with_top_logprobs(mut self, top_logprobs: Option<usize>) -> Self {
        self.top_logprobs = top_logprobs;
        self
    }

    /// Set max output tokens
    pub fn with_max_output_tokens(mut self, max_output_tokens: Option<usize>) -> Self {
        self.max_output_tokens = max_output_tokens;
        self
    }

    /// Set max tool calls
    pub fn with_max_tool_calls(mut self, max_tool_calls: Option<usize>) -> Self {
        self.max_tool_calls = max_tool_calls;
        self
    }

    /// Set store flag
    pub fn with_store(mut self, store: Option<bool>) -> Self {
        self.store = store;
        self
    }

    /// Set background flag
    pub fn with_background(mut self, background: Option<bool>) -> Self {
        self.background = background;
        self
    }

    /// Collect output text from all output items
    pub fn collect_output_text(&mut self) {
        let mut texts = Vec::new();
        for item in &self.output {
            if let OutputItem::Message { content, .. } = item {
                for c in content {
                    if let Some(text) = c.get_text() {
                        texts.push(text.to_string());
                    }
                }
            }
        }
        if !texts.is_empty() {
            self.output_text = Some(texts.join(""));
        }
    }
}

/// Tool definition for the OpenResponses API
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[serde(tag = "type")]
pub enum ResponseTool {
    /// Function tool
    #[serde(rename = "function")]
    Function {
        /// Function definition
        function: FunctionDefinition,
    },
}

/// Function definition for tools
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct FunctionDefinition {
    /// Name of the function
    pub name: String,
    /// Description of the function
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    /// JSON Schema for function parameters
    #[serde(skip_serializing_if = "Option::is_none")]
    pub parameters: Option<Value>,
    /// Whether strict mode is enabled
    #[serde(skip_serializing_if = "Option::is_none")]
    pub strict: Option<bool>,
}

/// Tool choice configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ResponseToolChoice {
    /// Simple choice
    Simple(SimpleToolChoice),
    /// Specific function
    Specific(SpecificToolChoice),
}

/// Simple tool choice options
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "lowercase")]
pub enum SimpleToolChoice {
    /// Let the model decide
    Auto,
    /// Don't use any tools
    None,
    /// Force tool use
    Required,
}

/// Specific tool choice
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct SpecificToolChoice {
    /// Tool type
    #[serde(rename = "type")]
    pub tool_type: String,
    /// Function to use
    pub function: SpecificFunctionChoice,
}

/// Specific function choice
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct SpecificFunctionChoice {
    /// Name of the function
    pub name: String,
}
