use super::agent_config::{AgentLoopConfig, AgentSharedState};
use super::api::{build_request_with_tools, call_openai_non_stream_lenient, create_openai_client};
use super::app::{StreamMsg, ToolResultMsg};
use super::compact;
use super::hook::{HookContext, HookEvent, HookManager};
use super::storage::{ChatMessage, ToolCallItem};
use crate::command::chat::constants::{
    ROLE_ASSISTANT, ROLE_TOOL, ROLE_USER, TODO_NAG_INTERVAL_ROUNDS,
};
use crate::command::chat::tools::Tool;
use crate::command::chat::tools::compact::CompactTool;
use crate::util::log::{write_error_log, write_info_log};
use crate::util::safe_lock;
use async_openai::types::chat::ChatCompletionTools;
use futures::StreamExt;
use std::sync::{Arc, Mutex, mpsc};

/// process_tool_calls 所需的通道和共享状态
struct ToolCallContext<'a> {
    tx: &'a mpsc::Sender<StreamMsg>,
    tool_result_rx: &'a mpsc::Receiver<ToolResultMsg>,
    pending_user_messages: &'a Arc<Mutex<Vec<ChatMessage>>>,
    hook_manager: &'a HookManager,
    supports_vision: bool,
    shared_messages: &'a Arc<Mutex<Vec<ChatMessage>>>,
    streaming_content: &'a Arc<Mutex<String>>,
}

/// 后台 Agent 循环：支持多轮工具调用
pub async fn run_agent_loop(
    config: AgentLoopConfig,
    shared: AgentSharedState,
    mut messages: Vec<ChatMessage>,
    tools: Vec<ChatCompletionTools>,
    system_prompt_fn: Arc<dyn Fn() -> Option<String> + Send + Sync>,
    tx: mpsc::Sender<StreamMsg>,
    tool_result_rx: mpsc::Receiver<ToolResultMsg>,
) {
    let AgentLoopConfig {
        provider,
        max_tool_rounds,
        compact_config,
        hook_manager,
        cancel_token,
    } = config;
    let AgentSharedState {
        streaming_content,
        pending_user_messages,
        background_manager,
        todo_manager,
        shared_messages,
        context_tokens,
    } = shared;

    let client = create_openai_client(&provider);

    let tool_ctx = ToolCallContext {
        tx: &tx,
        tool_result_rx: &tool_result_rx,
        pending_user_messages: &pending_user_messages,
        hook_manager: &hook_manager,
        supports_vision: provider.supports_vision,
        shared_messages: &shared_messages,
        streaming_content: &streaming_content,
    };

    for _round in 0..max_tool_rounds {
        // 每轮重新构建 system prompt（从磁盘读取最新配置）
        let mut system_prompt = system_prompt_fn();

        // 每轮开始时从待处理队列中 drain 用户在 agent loop 期间输入的新消息
        drain_pending_user_messages(&mut messages, &pending_user_messages);

        // ── Layer 1: micro_compact（替换旧 tool results）──
        // ── Layer 2: if tokens > threshold → auto_compact（LLM 摘要）──
        if compact_config.enabled {
            compact::micro_compact(&mut messages, compact_config.keep_recent);
            if compact::estimate_tokens(&messages) > compact_config.token_threshold {
                write_info_log(
                    "agent_loop",
                    "auto_compact triggered (token threshold exceeded)",
                );
                if let Err(e) = compact::auto_compact(&mut messages, &provider).await {
                    write_error_log("agent_loop", &format!("auto_compact failed: {}", e));
                }
            }
        }

        // Drain 后台任务完成通知，注入为系统提醒
        // 运行中的后台任务已通过 system prompt 的 {{.background_tasks}} 占位符注入，
        // 此处只处理完成通知（事件驱动，不适合放在模板中）
        {
            let notifications = background_manager.drain_notifications();
            for notif in notifications {
                let body = format!(
                    "<background_task_completed>\n<task_id>{}</task_id>\n<command>{}</command>\n<status>{}</status>\n<result>\n{}\n</result>\n</background_task_completed>",
                    notif.task_id, notif.command, notif.status, notif.result
                );
                push_system_reminder(&mut messages, body);
                write_info_log(
                    "BackgroundNotification",
                    &format!("注入后台任务完成通知: task_id={}", notif.task_id),
                );
            }
        }

        // 检查是否有待办事项
        todo_manager.increment_turn();
        if todo_manager.has_todos()
            && todo_manager.turns_since_last_call() >= TODO_NAG_INTERVAL_ROUNDS
        {
            let todos_summary = todo_manager.format_todos_summary();
            let body = format!(
                "<todo_reminder>\nYou have an active todo list but haven't updated it in 15+ rounds. Update it if progress has been made, or ignore this reminder if you are currently working on an item.\n<todos>\n{}\n</todos>\n</todo_reminder>",
                todos_summary
            );
            push_system_reminder(&mut messages, body);
            write_info_log(
                "TodoNagReminder",
                &format!("Injected nag reminder with todos:\n{}", todos_summary),
            );
        }

        // 清空流式内容缓冲（每轮开始时）
        {
            let mut sc = safe_lock(&streaming_content, "agent::streaming_content_clear");
            sc.clear();
        }

        // 记录请求输入日志
        {
            let mut log_content = String::new();
            if let Some(ref sp) = system_prompt {
                log_content.push_str(&format!("[System] {}\n", sp));
            }
            for msg in &messages {
                match msg.role.as_str() {
                    ROLE_ASSISTANT => {
                        if !msg.content.is_empty() {
                            log_content.push_str(&format!("[Assistant] {}\n", msg.content));
                        }
                        if let Some(ref tcs) = msg.tool_calls {
                            for tc in tcs {
                                log_content.push_str(&format!(
                                    "[Assistant/ToolCall] {}: {}\n",
                                    tc.name, tc.arguments
                                ));
                            }
                        }
                    }
                    ROLE_TOOL => {
                        let id = msg.tool_call_id.as_deref().unwrap_or("?");
                        let tool_name = msg
                            .tool_calls
                            .as_ref()
                            .and_then(|tc| tc.first())
                            .map(|tc| tc.name.as_str())
                            .unwrap_or("unknown");
                        log_content.push_str(&format!(
                            "[Tool/Result({} with id `{}`)] result:\n{}\n",
                            tool_name, id, msg.content
                        ));
                    }
                    ROLE_USER => {
                        log_content.push_str(&format!("[User] {}\n", msg.content));
                    }
                    other => {
                        log_content.push_str(&format!("[{}] {}\n", other, msg.content));
                    }
                }
            }
            write_info_log("Chat 请求", &log_content);
        }

        // ★ PreLlmRequest hook（可修改 messages 和 system_prompt）
        if hook_manager.has_hooks_for(HookEvent::PreLlmRequest) {
            let ctx = HookContext {
                event: HookEvent::PreLlmRequest,
                messages: Some(messages.clone()),
                system_prompt: system_prompt.clone(),
                model: Some(provider.model.clone()),
                cwd: std::env::current_dir()
                    .map(|p| p.display().to_string())
                    .unwrap_or_else(|_| ".".to_string()),
                ..Default::default()
            };
            if let Some(result) = hook_manager.execute(HookEvent::PreLlmRequest, ctx) {
                if result.abort {
                    let _ = tx.send(StreamMsg::Error("LLM 请求被 hook 中止".to_string()));
                    return;
                }
                if let Some(new_msgs) = result.messages {
                    messages = new_msgs;
                }
                if let Some(new_prompt) = result.system_prompt {
                    system_prompt = Some(new_prompt);
                }
                if let Some(inject) = result.inject_messages {
                    messages.extend(inject);
                }
            }
        }

        // 更新实际上下文 token 估算值（供 UI 显示）
        {
            let tokens = compact::estimate_tokens(&messages);
            if let Ok(mut ct) = context_tokens.lock() {
                *ct = tokens;
            }
        }

        // 记录本轮请求的消息统计
        {
            let has_images = messages
                .iter()
                .any(|m| m.images.as_ref().is_some_and(|imgs| !imgs.is_empty()));
            write_info_log(
                "agent_loop",
                &format!(
                    "第 {} 轮请求: messages={}, has_images={}, supports_vision={}",
                    _round,
                    messages.len(),
                    has_images,
                    provider.supports_vision
                ),
            );
        }

        let request = match build_request_with_tools(
            &provider,
            &messages,
            tools.clone(),
            system_prompt.as_deref(),
        ) {
            Ok(req) => {
                write_info_log("agent_loop", "build_request_with_tools 成功");
                req
            }
            Err(e) => {
                let _ = tx.send(StreamMsg::Error(format!("构建请求失败: {}", e)));
                return;
            }
        };

        {
            write_info_log("agent_loop", "开始创建流式请求...");
            let mut stream = match client.chat().create_stream(request.clone()).await {
                Ok(s) => {
                    write_info_log("agent_loop", "流式请求创建成功");
                    s
                }
                Err(e) => {
                    let error_msg = format!("API 请求失败: {}", e);
                    write_error_log("Chat API 流式请求创建", &error_msg);
                    let _ = tx.send(StreamMsg::Error(error_msg));
                    return;
                }
            };

            let mut finish_reason: Option<async_openai::types::chat::FinishReason> = None;
            let mut assistant_text = String::new();
            // 手动收集 tool_calls：按 index 聚合 (id, name, arguments)
            let mut raw_tool_calls: std::collections::BTreeMap<u32, (String, String, String)> =
                std::collections::BTreeMap::new();
            let mut stream_had_deserialize_error = false;

            let mut stream_chunk_count: u32 = 0;

            'stream: loop {
                tokio::select! {
                    result = stream.next() => {
                        match result {
                            Some(Ok(response)) => {
                                stream_chunk_count += 1;
                                // 记录前几个 chunk 的原始信息，便于调试
                                if stream_chunk_count <= 3 {
                                    let choices_debug: Vec<String> = response.choices.iter().map(|c| {
                                        format!(
                                            "idx={}, finish_reason={:?}, has_content={}, has_tool_calls={}",
                                            c.index,
                                            c.finish_reason,
                                            c.delta.content.is_some(),
                                            c.delta.tool_calls.is_some(),
                                        )
                                    }).collect();
                                    write_info_log(
                                        "stream_chunk",
                                        &format!("chunk #{}: choices=[{}]", stream_chunk_count, choices_debug.join("; ")),
                                    );
                                }
                                for choice in &response.choices {
                                    if let Some(ref content) = choice.delta.content {
                                        assistant_text.push_str(content);
                                        let mut sc = safe_lock(&streaming_content, "agent::stream_chunk");
                                        sc.push_str(content);
                                        drop(sc);
                                        let _ = tx.send(StreamMsg::Chunk);
                                    }
                                    // 尝试直接读取 tool_calls（若 async-openai 能反序列化）
                                    if let Some(ref toolcall_chunks) = choice.delta.tool_calls {
                                        for chunk in toolcall_chunks {
                                            let entry =
                                                raw_tool_calls.entry(chunk.index).or_insert_with(|| {
                                                    (
                                                        chunk.id.clone().unwrap_or_default(),
                                                        String::new(),
                                                        String::new(),
                                                    )
                                                });
                                            if entry.0.is_empty()
                                                && let Some(ref id) = chunk.id {
                                                    entry.0 = id.clone();
                                                }
                                            if let Some(ref tool_function) = chunk.function {
                                                if let Some(ref name) = tool_function.name {
                                                    entry.1.push_str(name);
                                                }
                                                if let Some(ref args) = tool_function.arguments {
                                                    entry.2.push_str(args);
                                                }
                                            }
                                        }
                                    }
                                    if let Some(ref fr) = choice.finish_reason {
                                        finish_reason = Some(*fr);
                                    }
                                }
                            }
                            Some(Err(e)) => {
                                let error_str = format!("{}", e);
                                write_error_log("Chat API 流式响应 error", &error_str);
                                // 检测是否是 tool_calls 反序列化错误（Gemini 等不返回 chunk index）
                                if error_str.contains("missing field `index`")
                                    || error_str.contains("tool_calls")
                                {
                                    // 标记需要用非流式重做，跳出流式循环
                                    stream_had_deserialize_error = true;
                                    break 'stream;
                                }
                                write_error_log("Chat API 流式响应", &error_str);
                                let _ = tx.send(StreamMsg::Error(error_str));
                                return;
                            }
                            None => {
                                write_info_log("agent_loop", "流式结束 (stream returned None)");
                                break 'stream;
                            }
                        }
                    }
                    _ = cancel_token.cancelled() => {
                        let _ = tx.send(StreamMsg::Cancelled);
                        return;
                    }
                }
            }

            // 记录流式回复日志
            if !assistant_text.is_empty() {
                write_info_log("Sprite 回复", &assistant_text);
            }

            write_info_log(
                "agent_loop",
                &format!(
                    "流式循环结束: finish_reason={:?}, assistant_text_len={}, raw_tool_calls={}, stream_had_deserialize_error={}",
                    finish_reason,
                    assistant_text.len(),
                    raw_tool_calls.len(),
                    stream_had_deserialize_error
                ),
            );

            // 如果流式遇到 tool_calls 反序列化错误，或者流式返回空响应（finish_reason=None 且无有效内容），
            // fallback 到非流式获取完整响应。
            // 常见场景：某些 API 对多模态+流式组合返回空 choices，需要非流式重试。
            let stream_empty =
                finish_reason.is_none() && assistant_text.is_empty() && raw_tool_calls.is_empty();
            if stream_had_deserialize_error || stream_empty {
                if stream_empty {
                    write_info_log(
                        "agent_loop",
                        &format!(
                            "流式返回空响应 (chunks={}, finish_reason=None, 无内容)，fallback 到非流式重试",
                            stream_chunk_count
                        ),
                    );
                }
                // 清空流式内容（切换到非流式）
                {
                    let mut sc = safe_lock(&streaming_content, "agent::fallback_clear");
                    sc.clear();
                }
                // 使用宽松反序列化的非流式调用（兼容非标准 finish_reason）
                let create_fut = call_openai_non_stream_lenient(&provider, &request);
                tokio::select! {
                    result = create_fut => match result {
                    Ok(fallback_result) => {
                        if fallback_result.is_tool_calls
                            && let Some(tool_items) = fallback_result.tool_calls {
                                    if tool_items.is_empty() {
                                        break;
                                    }
                                    let assistant_text =
                                        fallback_result.content.clone().unwrap_or_default();
                                    match process_tool_calls(
                                        tool_items,
                                        assistant_text,
                                        &mut messages,
                                        &tool_ctx,
                                    ) {
                                        Ok(compact_requested) => {
                                            // ── Layer 3: compact tool 触发 ──
                                            if compact_requested && compact_config.enabled {
                                                let _ = compact::auto_compact(&mut messages, &provider).await;
                                            }
                                            continue;
                                        }
                                        Err(()) => return,
                                    }
                                }
                        // 普通文本回复（或非标准 finish_reason 如 network_error）
                        if let Some(ref content) = fallback_result.content
                            && !content.is_empty()
                        {
                                write_info_log("Sprite 回复", content);
                                let mut sc = safe_lock(&streaming_content, "agent::fallback_content");
                                sc.push_str(content);
                                drop(sc);
                                let _ = tx.send(StreamMsg::Chunk);
                        }
                        // 非标准 finish_reason 且无内容时，报告错误
                        if let Some(ref reason) = fallback_result.finish_reason
                            && !matches!(reason.as_str(), "stop" | "length" | "tool_calls" | "content_filter" | "function_call")
                            && fallback_result.content.as_deref().unwrap_or_default().is_empty()
                        {
                                let error_msg = format!("API 返回异常: finish_reason={}", reason);
                                write_error_log("Sprite API fallback 非流式", &error_msg);
                                let _ = tx.send(StreamMsg::Error(error_msg));
                                return;
                        }
                    }
                    Err(e) => {
                        let error_msg = format!("API 请求失败(fallback): {}", e);
                        write_error_log("Sprite API fallback 非流式", &error_msg);
                        let _ = tx.send(StreamMsg::Error(error_msg));
                        return;
                    }
                },
                    _ = cancel_token.cancelled() => {
                        let _ = tx.send(StreamMsg::Cancelled);
                        return;
                    }
                }
                // fallback 非流式正常结束，但如果有用户增量消息则继续循环
                if !safe_lock(&pending_user_messages, "agent::pending_check_fallback").is_empty() {
                    flush_streaming_as_message(&streaming_content, &mut messages, &shared_messages);
                    continue;
                }
                break;
            }

            // 检查流式模式下的 tool_calls finish_reason
            let is_tool_calls = matches!(
                finish_reason,
                Some(async_openai::types::chat::FinishReason::ToolCalls)
            );

            if is_tool_calls && !raw_tool_calls.is_empty() {
                let tool_items: Vec<ToolCallItem> = raw_tool_calls
                    .into_values()
                    .map(|(id, name, arguments)| ToolCallItem {
                        id,
                        name,
                        arguments,
                    })
                    .collect();

                if tool_items.is_empty() {
                    break;
                }

                match process_tool_calls(tool_items, assistant_text, &mut messages, &tool_ctx) {
                    Ok(compact_requested) => {
                        // ── Layer 3: compact tool 触发 ──
                        if compact_requested && compact_config.enabled {
                            let _ = compact::auto_compact(&mut messages, &provider).await;
                        }
                        continue;
                    }
                    Err(()) => return,
                }
            } else {
                // 正常结束，但如果有用户增量消息则继续循环
                if !safe_lock(&pending_user_messages, "agent::pending_check_stream").is_empty() {
                    flush_streaming_as_message(&streaming_content, &mut messages, &shared_messages);
                    continue;
                }
                break;
            }
        }
    }

    let _ = tx.send(StreamMsg::Done);
}

/// 在 agent loop 中注入"系统级提醒"消息
/// 内容会被自动包在 <system-reminder>...</system-reminder> 中
/// 统一使用 ROLE_USER —— `<system-reminder>` 标签本身就告诉 LLM "这是系统注入的事实"，
/// 而 ROLE_USER 不需要 tool_call_id，跨 provider 兼容性最好
fn push_system_reminder(messages: &mut Vec<ChatMessage>, body: impl Into<String>) {
    messages.push(ChatMessage {
        role: ROLE_USER.to_string(),
        content: format!("<system-reminder>\n{}\n</system-reminder>", body.into()),
        tool_calls: None,
        tool_call_id: None,
        images: None,
    });
}

/// 从待处理队列中 drain 用户在 agent loop 期间发送的新消息，追加到 messages
fn drain_pending_user_messages(
    messages: &mut Vec<ChatMessage>,
    pending_user_messages: &Arc<Mutex<Vec<ChatMessage>>>,
) {
    let mut pending = safe_lock(pending_user_messages, "agent::drain_pending");
    if !pending.is_empty() {
        // 给每条追加的用户消息添加 [User appended] 标记
        for msg in pending.iter_mut() {
            if msg.role == "user" {
                msg.content = format!("[User appended] {}", msg.content);
            }
        }
        messages.append(&mut *pending);
    }
}

/// 向共享消息列表中追加一条消息（agent 线程写入，UI 线程读取）
fn push_shared(shared: &Arc<Mutex<Vec<ChatMessage>>>, msg: ChatMessage) {
    if let Ok(mut msgs) = shared.lock() {
        msgs.push(msg);
    }
}

/// 将 streaming_content 中的文本保存为 assistant 消息（多轮 agent loop 中间轮的文本回复）
/// 调用后 streaming_content 被清空，避免 UI 侧 finish_loading 再次保存导致重复
fn flush_streaming_as_message(
    streaming_content: &Arc<Mutex<String>>,
    messages: &mut Vec<ChatMessage>,
    shared_messages: &Arc<Mutex<Vec<ChatMessage>>>,
) {
    let mut sc = safe_lock(streaming_content, "agent::flush_streaming");
    if !sc.is_empty() {
        let text_msg = ChatMessage {
            role: ROLE_ASSISTANT.to_string(),
            content: sc.clone(),
            tool_calls: None,
            tool_call_id: None,
            images: None,
        };
        messages.push(text_msg.clone());
        push_shared(shared_messages, text_msg);
        sc.clear();
    }
}

/// 记录工具调用请求日志
fn log_tool_request(tool_items: &[ToolCallItem]) {
    let mut log_content = String::new();
    for item in tool_items {
        log_content.push_str(&format!("- {}: {}\n", item.name, item.arguments));
    }
    write_info_log("工具调用请求", &log_content);
}

/// 记录工具调用结果日志
fn log_tool_results(tool_items: &[ToolCallItem], tool_results: &[ToolResultMsg]) {
    let mut log_content = String::new();
    for (i, result) in tool_results.iter().enumerate() {
        let (tool_name, tool_args) = tool_items
            .get(i)
            .map(|t| (t.name.as_str(), t.arguments.as_str()))
            .unwrap_or(("unknown", ""));
        log_content.push_str(&format!(
            "- [{}] {}({}): {}\n",
            result.tool_call_id, tool_name, tool_args, result.result
        ));
    }
    write_info_log("工具调用结果", &log_content);
}

/// 处理工具调用的公共逻辑：发送请求、等待结果、更新 messages
/// 返回 Ok(bool) 表示成功（应 continue 循环），bool 为 true 时表示有 compact tool 被调用
/// Err(()) 表示 channel 断开（应 return）
fn process_tool_calls(
    tool_items: Vec<ToolCallItem>,
    assistant_text: String,
    messages: &mut Vec<ChatMessage>,
    ctx: &ToolCallContext<'_>,
) -> Result<bool, ()> {
    log_tool_request(&tool_items);

    if !assistant_text.is_empty() {
        write_info_log("Sprite 回复", &assistant_text);
    }

    // 检查是否有 compact tool 被调用
    let compact_requested = tool_items.iter().any(|t| t.name == CompactTool {}.name());

    // ★ 如果 LLM 同时返回了文本和 tool_calls，拆成两条消息：
    //   1. 纯文本 assistant 消息（让 UI 先渲染文字）
    //   2. tool_call assistant 消息（content 为空，只带 tool_calls）
    //   这样渲染时文字在上面，tool_call 在下面
    if !assistant_text.is_empty() {
        let text_msg = ChatMessage {
            role: ROLE_ASSISTANT.to_string(),
            content: assistant_text,
            tool_calls: None,
            tool_call_id: None,
            images: None,
        };
        messages.push(text_msg.clone());
        push_shared(ctx.shared_messages, text_msg);
        // 清空 streaming_content，文本已保存，避免 UI 继续显示流式内容
        if let Ok(mut sc) = ctx.streaming_content.lock() {
            sc.clear();
        }
    }

    let tool_call_msg = ChatMessage {
        role: ROLE_ASSISTANT.to_string(),
        content: String::new(),
        tool_calls: Some(tool_items.clone()),
        tool_call_id: None,
        images: None,
    };
    messages.push(tool_call_msg.clone());
    push_shared(ctx.shared_messages, tool_call_msg);

    if ctx
        .tx
        .send(StreamMsg::ToolCallRequest(tool_items.clone()))
        .is_err()
    {
        return Err(());
    }

    let mut tool_results: Vec<ToolResultMsg> = Vec::new();
    for _ in &tool_items {
        match ctx.tool_result_rx.recv() {
            Ok(result) => tool_results.push(result),
            Err(_) => return Err(()),
        }
    }

    log_tool_results(&tool_items, &tool_results);

    // 收集需要延迟注入的图片消息（在所有 tool results 之后统一注入，
    // 避免在 tool results 中间插入 user 消息导致 API 报错）
    let mut deferred_image_msgs: Vec<ChatMessage> = Vec::new();

    for result in tool_results {
        let mut result_content = result.result;
        let result_images = result.images;

        // 查找工具名
        let tool_name = tool_items
            .iter()
            .find(|t| t.id == result.tool_call_id)
            .map(|t| t.name.clone());

        // ★ PostToolExecution hook
        if ctx.hook_manager.has_hooks_for(HookEvent::PostToolExecution) {
            let hook_ctx = HookContext {
                event: HookEvent::PostToolExecution,
                tool_name: tool_name.clone(),
                tool_result: Some(result_content.clone()),
                cwd: std::env::current_dir()
                    .map(|p| p.display().to_string())
                    .unwrap_or_else(|_| ".".to_string()),
                ..Default::default()
            };
            if let Some(hook_result) = ctx
                .hook_manager
                .execute(HookEvent::PostToolExecution, hook_ctx)
                && let Some(new_result) = hook_result.tool_result
            {
                result_content = new_result;
            }
        }

        let tool_msg = ChatMessage {
            role: ROLE_TOOL.to_string(),
            content: result_content,
            tool_calls: None,
            tool_call_id: Some(result.tool_call_id.clone()),
            images: None,
        };
        messages.push(tool_msg.clone());
        push_shared(ctx.shared_messages, tool_msg);

        // 如果模型支持视觉且工具返回了图片，先收集，稍后统一注入
        if !result_images.is_empty() {
            let tool_label = tool_name.as_deref().unwrap_or("unknown");
            let img_count = result_images.len();
            write_info_log(
                "ImageInjection",
                &format!(
                    "工具 {} 返回了 {} 张图片, supports_vision={}",
                    tool_label, img_count, ctx.supports_vision
                ),
            );
            if ctx.supports_vision {
                let img_msg = ChatMessage {
                    role: ROLE_USER.to_string(),
                    content: format!(
                        "[{tool_label} 返回了 {img_count} 张图片，请查看图片内容并继续帮助完成任务]"
                    ),
                    tool_calls: None,
                    tool_call_id: None,
                    images: Some(
                        result_images
                            .into_iter()
                            .map(|img| super::storage::ImageData {
                                base64: img.base64,
                                media_type: img.media_type,
                            })
                            .collect(),
                    ),
                };
                deferred_image_msgs.push(img_msg);
            } else {
                write_info_log(
                    "ImageInjection",
                    &format!(
                        "supports_vision=false，丢弃 {} 返回的 {} 张图片",
                        tool_label, img_count
                    ),
                );
            }
        }
    }

    // ★ 所有 tool results 处理完毕后，统一注入图片 user messages
    if !deferred_image_msgs.is_empty() {
        write_info_log(
            "ImageInjection",
            &format!(
                "在所有 tool results 之后注入 {} 条图片消息",
                deferred_image_msgs.len()
            ),
        );
        for img_msg in deferred_image_msgs {
            // 只加入 LLM 上下文，不推送到 shared_messages（避免 UI 渲染这条内部消息）
            messages.push(img_msg);
        }
    }

    drain_pending_user_messages(messages, ctx.pending_user_messages);
    Ok(compact_requested)
}
