//! Anthropic 协议适配 — 协议数据结构与 SSE 事件解析（纯函数）。
//!
//! 本模块从 `anthropic_compat.rs` 抽离，承载与 HTTP 无关的纯协议逻辑：
//! - 请求/响应结构体（`AnthropicRequest` 等）
//! - 协议常量（`ANTHROPIC_VERSION` / `DEFAULT_MAX_TOKENS`）
//! - SSE 事件 → `StreamChunk` 转换纯函数（`apply_anthropic_event`）
//!
//! Provider struct + impl（含 HTTP 调用）仍留在 `anthropic_compat.rs`，
//! Rust impl 块不可跨文件，故仅搬迁 impl 块外部的类型/常量/纯函数。
//! 零行为变更（纯搬迁）。

use serde::{Deserialize, Serialize};
use tracing::{error, warn};

use crate::provider::{tool_call_id_or_fallback, StreamChunk, TokenUsage, ToolCallDelta};

// ============================================================
// Anthropic API 请求/响应结构体
// ============================================================

/// Anthropic 请求体
#[derive(Debug, Clone, Serialize)]
pub(crate) struct AnthropicRequest {
    pub model: String,
    pub messages: Vec<serde_json::Value>,
    pub max_tokens: u32,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub system: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub temperature: Option<f32>,
    pub stream: bool,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tools: Option<Vec<AnthropicToolDef>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tool_choice: Option<serde_json::Value>,
}

/// Anthropic 工具定义（input_schema 对应 OpenAI 的 parameters）
#[derive(Debug, Clone, Serialize)]
pub(crate) struct AnthropicToolDef {
    pub name: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    pub input_schema: serde_json::Value,
}

/// Anthropic 同步响应
#[derive(Debug, Deserialize)]
pub(crate) struct AnthropicResponse {
    // SW-260618-25: Anthropic 响应反序列化字段,保留以对齐响应结构(响应 id),标注意图消除 dead_code warning
    #[allow(dead_code)]
    id: String,
    pub model: String,
    pub content: Vec<AnthropicContentBlock>,
    // SW-260618-25: Anthropic 响应反序列化字段,保留以备调试/未来消费(如日志记录调用终止原因),标注意图消除 dead_code warning
    #[allow(dead_code)]
    stop_reason: Option<String>,
    pub usage: AnthropicUsage,
}

/// 响应 content 块（text 或 tool_use）
#[derive(Debug, Deserialize)]
pub(crate) struct AnthropicContentBlock {
    #[serde(rename = "type")]
    pub block_type: String,
    #[serde(default)]
    pub text: Option<String>,
    /// tool_use 块字段
    pub id: Option<String>,
    pub name: Option<String>,
    pub input: Option<serde_json::Value>,
}

#[derive(Debug, Deserialize)]
pub(crate) struct AnthropicUsage {
    pub input_tokens: u32,
    pub output_tokens: u32,
}

// ============================================================
// 协议常量
// ============================================================

/// Anthropic 流式协议版本头
pub(crate) const ANTHROPIC_VERSION: &str = "2023-06-01";
/// Anthropic max_tokens 必填，缺省时的兜底值
pub(crate) const DEFAULT_MAX_TOKENS: u32 = 4096;

// ============================================================
// SSE 解析纯函数（与 HTTP 解耦，便于单测）
// ============================================================

/// 将一条 Anthropic Messages SSE 事件 data 解析为 StreamChunk，并按需更新 usage 累加器。
///
/// 按 `type` 字段分发：
/// - `message_start` → 用 `message.usage.input_tokens` 初始化累加器（output 置 0）。
/// - `message_delta` → **output_tokens 是累计值（非增量）**，直接覆盖 `completion_tokens` 并重算 `total`。
/// - `content_block_delta` (text_delta/input_json_delta) → 文本/工具入参增量。
/// - `content_block_start` (tool_use) → 工具块开始，带 id+name。
/// - `message_stop` → 返回 `finished=true` 终态 chunk，`usage` 取自累加器（`take()`）。
/// - `error` → 返回 `finished=true` 终态空 chunk。
/// - 其它（content_block_stop / ping 等）→ 空 chunk。
///
/// 等价性：content_block / message_stop / error 等事件分支与原 stream() 闭包逐字一致；
/// usage 透传（message_stop 终态 take() 带出、message_delta 的 output_tokens 按累计值覆盖）
/// 为本次新增能力，对应 StreamChunk 新增的 usage 字段。
pub(crate) fn apply_anthropic_event(data: &str, usage_accum: &mut Option<TokenUsage>) -> StreamChunk {
    // 解析 data 中的 JSON，按 type 字段决定如何转 StreamChunk
    let v: serde_json::Value = match serde_json::from_str(data) {
        Ok(v) => v,
        Err(_) => {
            return StreamChunk { delta: String::new(), finished: false, tool_calls: None, usage: None, error: None, reasoning_content: None }
        }
    };
    let ty = v.get("type").and_then(|t| t.as_str()).unwrap_or("");
    match ty {
        // 消息开始：取 input_tokens 初始化累积器（output 此时未知，置 0）
        "message_start" => {
            if let Some(inp) = v
                .get("message")
                .and_then(|m| m.get("usage"))
                .and_then(|u| u.get("input_tokens"))
                .and_then(|t| t.as_u64())
            {
                *usage_accum = Some(TokenUsage {
                    prompt_tokens: inp as u32,
                    completion_tokens: 0,
                    total_tokens: inp as u32,
                });
            }
            StreamChunk { delta: String::new(), finished: false, tool_calls: None, usage: None, error: None, reasoning_content: None }
        }
        // 消息增量：output_tokens 是累计值（非增量），直接覆盖 completion + 重算 total
        "message_delta" => {
            if let Some(out) = v.get("usage").and_then(|u| u.get("output_tokens")).and_then(|t| t.as_u64()) {
                let acc = usage_accum
                    .get_or_insert(TokenUsage { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 });
                acc.completion_tokens = out as u32;
                acc.total_tokens = acc.prompt_tokens + acc.completion_tokens;
            }
            StreamChunk { delta: String::new(), finished: false, tool_calls: None, usage: None, error: None, reasoning_content: None }
        }
        // 文本增量
        "content_block_delta" => {
            if let Some(delta) = v.get("delta") {
                if delta.get("type").and_then(|t| t.as_str()) == Some("text_delta") {
                    let text = delta.get("text").and_then(|t| t.as_str()).unwrap_or("").to_string();
                    return StreamChunk { delta: text, finished: false, tool_calls: None, usage: None, error: None, reasoning_content: None };
                }
                // 工具入参增量
                if delta.get("type").and_then(|t| t.as_str()) == Some("input_json_delta") {
                    let partial = delta.get("partial_json").and_then(|t| t.as_str()).unwrap_or("").to_string();
                    let idx = v.get("index").and_then(|i| i.as_u64()).unwrap_or(0) as u32;
                    return StreamChunk {
                        delta: String::new(),
                        finished: false,
                        tool_calls: Some(vec![ToolCallDelta {
                            index: idx,
                            id: None,
                            function_name: None,
                            function_arguments: Some(partial),
                        }]),
                        usage: None,
                        error: None,
                        reasoning_content: None,
                    };
                }
            }
            StreamChunk { delta: String::new(), finished: false, tool_calls: None, usage: None, error: None, reasoning_content: None }
        }
        // 工具块开始：带 id + name
        "content_block_start" => {
            if let Some(cb) = v.get("content_block") {
                if cb.get("type").and_then(|t| t.as_str()) == Some("tool_use") {
                    let idx = v.get("index").and_then(|i| i.as_u64()).unwrap_or(0) as u32;
                    let name = cb.get("name").and_then(|t| t.as_str()).map(|s| s.to_string());
                    // CR-空 id：id 缺失/空时用 fallback 兜底（流式后续 input_json_delta 按 index 累加，
                    // 中途无法整体跳过）。与同步路径 + OpenAI 路径共用 tool_call_id_or_fallback（DRY），
                    // prefix=`gen_anthropic_stream` 区分来源。非空原样。
                    let raw_id = cb.get("id").and_then(|t| t.as_str()).unwrap_or("");
                    let id = if raw_id.is_empty() {
                        let fallback = tool_call_id_or_fallback(raw_id, idx as usize, "gen_anthropic_stream");
                        warn!(%fallback, name = ?name, "Anthropic 流式 tool_use 块 id 为空，已生成 fallback id（原样回传会触发 GLM 500）");
                        Some(fallback)
                    } else {
                        Some(raw_id.to_string())
                    };
                    return StreamChunk {
                        delta: String::new(),
                        finished: false,
                        tool_calls: Some(vec![ToolCallDelta {
                            index: idx,
                            id,
                            function_name: name,
                            function_arguments: None,
                        }]),
                        usage: None,
                        error: None,
                        reasoning_content: None,
                    };
                }
            }
            StreamChunk { delta: String::new(), finished: false, tool_calls: None, usage: None, error: None, reasoning_content: None }
        }
        // 消息结束：带出累积 usage
        "message_stop" => StreamChunk {
            delta: String::new(),
            finished: true,
            tool_calls: None,
            usage: usage_accum.take(),
            error: None,
            reasoning_content: None,
        },
        // 错误事件：流中途出错。不走 finished 完成路径（避免残缺响应被当正常完成入库），
        // 改由 stream_llm 识别 error 非空 → 发 AiError + 丢弃残缺（与 OpenAI 路径 Err 一致）。
        "error" => {
            let msg = v.get("error").and_then(|e| e.get("message")).and_then(|m| m.as_str()).unwrap_or("stream error").to_string();
            error!(%msg, "Anthropic 流式错误事件");
            StreamChunk { delta: String::new(), finished: false, tool_calls: None, usage: None, error: Some(msg), reasoning_content: None }
        }
        // content_block_stop / ping 等不产出 chunk
        _ => StreamChunk { delta: String::new(), finished: false, tool_calls: None, usage: None, error: None, reasoning_content: None },
    }
}
