//! Golden structs for OpenAI Chat Completions API spec compliance.
//!
//! These structs are derived directly from the OpenAI API documentation and
//! represent the exact response shape a real OpenAI API returns. They are
//! test-only — not shipped in the library.
//!
//! Spec: https://platform.openai.com/docs/api-reference/chat/object
//! Streaming: https://platform.openai.com/docs/api-reference/chat/streaming
//! Create: https://platform.openai.com/docs/api-reference/chat/create

use serde::Deserialize;

// ---------------------------------------------------------------------------
// Non-streaming response
// ---------------------------------------------------------------------------

/// The chat completion object.
/// Spec: https://platform.openai.com/docs/api-reference/chat/object
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SpecChatCompletion {
    /// 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,
    /// This fingerprint represents the backend configuration that the model
    /// runs with. Can be used with the `seed` parameter for determinism.
    pub system_fingerprint: Option<String>,
    /// The service tier used for processing the request.
    pub service_tier: Option<String>,
    /// A list of chat completion choices.
    pub choices: Vec<SpecChoice>,
    /// Usage statistics for the completion request.
    pub usage: SpecUsage,
}

/// A choice in the chat completion response.
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SpecChoice {
    /// The index of the choice in the list of choices.
    pub index: u64,
    /// A chat completion message generated by the model.
    pub message: SpecMessage,
    /// The reason the model stopped generating tokens.
    pub finish_reason: String,
    /// Log probability information for the choice.
    pub logprobs: Option<serde_json::Value>,
}

/// A chat completion message.
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SpecMessage {
    /// The role of the author of this message (always `assistant`).
    pub role: String,
    /// The contents of the message.
    pub content: Option<String>,
    /// The tool calls generated by the model.
    pub tool_calls: Option<Vec<SpecToolCall>>,
    /// The refusal message generated by the model (content filtering).
    pub refusal: Option<String>,
}

/// A tool call in the chat completion response.
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SpecToolCall {
    /// The ID of the tool call.
    pub id: String,
    /// The type of the tool (always `function`).
    #[serde(rename = "type")]
    pub call_type: String,
    /// The function that the model called.
    pub function: SpecFunction,
}

/// A function call within a tool call.
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SpecFunction {
    /// The name of the function to call.
    pub name: String,
    /// The arguments to call the function with, as a JSON string.
    pub arguments: String,
}

/// Usage statistics for a completion request.
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SpecUsage {
    /// 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,
}

// ---------------------------------------------------------------------------
// Error response
// ---------------------------------------------------------------------------

/// OpenAI error response envelope.
/// Spec: https://platform.openai.com/docs/guides/error-codes
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SpecErrorResponse {
    pub error: SpecError,
}

/// OpenAI error object.
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SpecError {
    pub message: String,
    #[serde(rename = "type")]
    pub error_type: String,
    pub param: Option<serde_json::Value>,
    pub code: Option<String>,
}

// ---------------------------------------------------------------------------
// Streaming response (chunks)
// ---------------------------------------------------------------------------

/// A streamed chunk of a chat completion response.
/// Spec: https://platform.openai.com/docs/api-reference/chat/streaming
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SpecChatCompletionChunk {
    /// A unique identifier for the chat completion. Shared across all chunks.
    pub id: String,
    /// The object type, which is always `chat.completion.chunk`.
    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,
    /// Backend configuration fingerprint for determinism.
    pub system_fingerprint: Option<String>,
    /// The service tier used for processing the request.
    pub service_tier: Option<String>,
    /// A list of chat completion choices (deltas).
    pub choices: Vec<SpecChunkChoice>,
    // OpenAI supports optional `usage` on final chunk (stream_options.include_usage).
    // Accepted here for forward-compat — not yet emitted by server.
    #[serde(default)]
    pub usage: Option<SpecUsage>,
}

/// A choice delta in a streaming chunk.
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SpecChunkChoice {
    /// The index of the choice in the list.
    pub index: u64,
    /// The delta (incremental content change).
    pub delta: SpecDelta,
    /// The reason the model stopped generating (only on final content chunk).
    pub finish_reason: Option<String>,
    /// Log probability information for this delta.
    pub logprobs: Option<serde_json::Value>,
}

/// Incremental content change in a streaming chunk.
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SpecDelta {
    /// The role of the author (only on first chunk).
    pub role: Option<String>,
    /// Incremental text content.
    pub content: Option<String>,
    /// Incremental tool calls.
    pub tool_calls: Option<Vec<SpecToolCallDelta>>,
    /// Incremental refusal content.
    pub refusal: Option<String>,
}

/// A tool call delta in streaming — index is required to identify the tool call.
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SpecToolCallDelta {
    /// Index of the tool call this delta belongs to (required in streaming).
    pub index: u64,
    /// The ID of the tool call (present on first delta for this tool).
    pub id: Option<String>,
    /// The type of tool (present on first delta for this tool).
    #[serde(rename = "type")]
    pub call_type: Option<String>,
    /// The function delta.
    pub function: Option<SpecFunctionDelta>,
}

/// Incremental function call data in streaming.
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SpecFunctionDelta {
    /// The function name (present on first delta).
    pub name: Option<String>,
    /// Incremental argument string.
    pub arguments: Option<String>,
}
