package com.emoon.openplatform.controller;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.emoon.common.core.domain.R;
import com.emoon.common.core.utils.StringUtils;
import com.emoon.openplatform.domain.AskInfoDo;
import com.emoon.openplatform.domain.SysProjectDo;
import com.emoon.openplatform.domain.dto.request.SignRequest;
import com.emoon.openplatform.domain.dto.request.SseRequest;
import com.emoon.openplatform.domain.dto.resp.CheckResp;
import com.emoon.openplatform.enums.SystemEnum;
import com.emoon.openplatform.service.IAskInfoService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import jakarta.servlet.http.HttpServletResponse;

import java.io.*;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * @author destiny
 * @description: SSE 流式响应接口
 * @date 2025/12/10
 */
@Slf4j
@RestController
@RequestMapping("/chat")
@CrossOrigin(origins = "*")
@RequiredArgsConstructor
public class ApiSseController {

    @Value("${emoon.matching.url:http://8.136.61.90:5666/api}")
    private String emoonMatchingUrl;

    @Value("${emoon.matching.username:test}")
    private String emoonMatchingUsername;

    @Value("${emoon.matching.password:test123}")
    private String emoonMatchingPassword;

    @Value("${emoon.matching.maxDistance:0.7}")
    private Double emoonMatchingMaxDistance;

    @Value("${deepseek.api.key:sk-ba46ccfb4e184a74af747246d1f0916e}")
    private String deepseekApiKey;

    private final CheckController checkController;

    private final IAskInfoService askInfoService;

    /**
     * 多模态响应接口（POST请求，使用DTO接收参数）
     * 支持多种响应类型：SSE流式、HTTP普通JSON、语音二进制、图片二进制等
     * @param request SSE请求参数DTO
     * @param timestamp 请求时间戳（毫秒）
     * @param requestId 客户端唯一请求ID
     * @param publicKey 平台公钥
     * @param sign 签名串（MD5编码）
     * @param response HTTP响应对象
     * @return 根据responseType返回不同类型的响应对象
     */
    @PostMapping(value = "/apisse")
    public Object apisse(@RequestBody @Valid SseRequest request,
                          @RequestHeader(value = "X-Emoon-Timestamp", required = false) Long timestamp,
                          @RequestHeader(value = "X-Emoon-Request-Id", required = false) String requestId,
                          @RequestHeader(value = "X-Emoon-Public-Key", required = false) String publicKey,
                          @RequestHeader(value = "X-Emoon-Sign", required = false) String sign,
                          HttpServletResponse response) {

        // 验证必需的请求头
        if (timestamp == null || requestId == null || publicKey == null || sign == null) {
            return R.fail(SystemEnum.ILLEGAL_REQUEST.getCode(), "请求头验证失败：缺少必需的请求头");
        }

        // 1. 验证权限
        R<CheckResp> checkResult = checkController.check(
                request.getModel(),
                request.getRagId(),
                request.getMcpId(),
                publicKey,
                request.getPromptTemplateId()
        );

        // 如果check失败，直接返回错误
        if (checkResult == null || !R.isSuccess(checkResult)) {
            return checkResult != null ? checkResult : R.fail(SystemEnum.ILLEGAL_REQUEST.getCode(), "权限验证失败");
        }

        // 2. 从check结果中获取privateKey
        CheckResp checkResp = checkResult.getData();
        if (checkResp == null || checkResp.getSysProjectList() == null || checkResp.getSysProjectList().isEmpty()) {
            return R.fail(SystemEnum.PROJECT_NOT_EXISTS.getCode(), SystemEnum.PROJECT_NOT_EXISTS.getMessage());
        }

        SysProjectDo sysProject = checkResp.getSysProjectList().get(0);
        String privateKey = sysProject.getPrivateKey();
        if (StringUtils.isBlank(privateKey)) {
            return R.fail(SystemEnum.ILLEGAL_REQUEST.getCode(), "项目私钥不存在");
        }

        // 3. 生成签名并验证
        String payload = request.getPayload() != null ? request.getPayload() : "";
        String generatedSign = generateSignInternal(requestId, String.valueOf(timestamp), payload, privateKey);

        // 4. 比较签名是否一致
        if (!sign.equals(generatedSign)) {
            log.warn("签名验证失败 - 请求签名: {}, 生成签名: {}", sign, generatedSign);
            return R.fail(SystemEnum.ILLEGAL_REQUEST.getCode(), "签名验证失败，请检查签名是否正确");
        }

        log.info("签名验证成功");

        // 5. 验证时间戳是否在有效期内（5分钟内）
        long currentTimestamp = System.currentTimeMillis(); // 当前时间戳（毫秒）
        long timeDiff = Math.abs(currentTimestamp - timestamp); // 时间差（毫秒）
        long maxAllowedDiff = 5 * 60 * 1000; // 5分钟 = 300000毫秒

        if (timeDiff > maxAllowedDiff) {
            log.warn("时间戳验证失败 - 请求时间戳: {}, 当前时间戳: {}, 时间差: {}毫秒, 最大允许: {}毫秒",
                    timestamp, currentTimestamp, timeDiff, maxAllowedDiff);
            return R.fail(SystemEnum.ILLEGAL_REQUEST.getCode(), "请求已过期。");
        }

        log.info("时间戳验证成功 - 请求时间戳: {}, 当前时间戳: {}, 时间差: {}毫秒",
                timestamp, currentTimestamp, timeDiff);

        // 6. 处理prompt占位符替换（使用prompt_template_id从数据库查询到的prompt模板）
        if (checkResp.getSysPrompt() != null && StringUtils.isNotBlank(checkResp.getSysPrompt().getPrompt())) {
            String originalPrompt = checkResp.getSysPrompt().getPrompt();
            String processedPrompt = processPromptPlaceholders(
                    originalPrompt,
                    request.getPayload()
            );
            // 更新prompt内容
            checkResp.getSysPrompt().setPrompt(processedPrompt);
            log.info("Prompt占位符处理完成（prompt_template_id: {}），原始长度: {}, 处理后长度: {}",
                    request.getPromptTemplateId(),
                    originalPrompt.length(),
                    processedPrompt.length());
        }

        // 6. 如果存在mcpId，直接执行MCP方法
        if (StringUtils.isNotBlank(request.getMcpId())) {
            log.info("检测到mcpId，执行MCP方法 - mcpId: {}", request.getMcpId());
            return handleMcpRequest(request, response, checkResp, sign);
        }

        // 7. 根据model决定返回类型
        String model = request.getModel();
        log.info("根据model判断返回类型 - model: {}", model);

        // 如果model为qwen3-32B，使用SSE流式响应
        if ("qwen3-32B".equalsIgnoreCase(model)) {
            return handleSseResponse(request, response, checkResp, sign);
        }

        // 如果model为deepseek-chat，使用SSE流式响应
        if ("deepseek-chat".equalsIgnoreCase(model)) {
            return handleSseResponse(request, response, checkResp, sign);
        }

        // 如果model为whisper-large-v3，使用JSON响应（音频模型）
        if ("whisper-large-v3".equalsIgnoreCase(model)) {
            return handleAudioModelRequest(request, checkResp);
        }

        // 其他模型暂不支持，返回错误
        return R.fail(SystemEnum.ILLEGAL_REQUEST.getCode(),
                     "当前仅支持qwen3-32B、deepseek-chat和whisper-large-v3模型，model: " + (model != null ? model : "null"));
    }

    /**
     * 处理SSE流式响应
     */
    private SseEmitter handleSseResponse(SseRequest request, HttpServletResponse response, CheckResp checkResp, String sign) {
        // 确保响应头设置UTF-8编码
        response.setCharacterEncoding("UTF-8");
        response.setContentType(MediaType.TEXT_EVENT_STREAM_VALUE + ";charset=UTF-8");

        SseEmitter emitter = new SseEmitter(0L); // 0 表示不超时

        // 异步处理流式响应
        CompletableFuture.runAsync(() -> {
            try {
                processSseStreamFromRequest(emitter, request, checkResp, sign);
            } catch (Exception e) {
                log.error("SSE流处理异常", e);
                try {
                    emitter.send(SseEmitter.event()
                        .name("error")
                        .data("{\"error\":\"" + e.getMessage() + "\"}"));
                    emitter.completeWithError(e);
                } catch (IOException ex) {
                    log.error("发送错误信息失败", ex);
                }
            }
        });

        return emitter;
    }

    /**
     * 基于SseRequest处理SSE流式响应
     */
    private void processSseStreamFromRequest(SseEmitter emitter, SseRequest request, CheckResp checkResp, String sign) throws IOException {
        // 1. 获取请求参数
        String model = request.getModel();
        List<Map<String, Object>> messages = request.getMessages();
        Boolean stream = request.getStream() != null ? request.getStream() : true;
        Double temperature = request.getTemperature();

        log.info("处理SSE流式请求 - model: {}, stream: {}", model, stream);

        // 1.1 获取处理后的prompt（如果存在）
        String processedPrompt = null;
        if (checkResp != null && checkResp.getSysPrompt() != null && StringUtils.isNotBlank(checkResp.getSysPrompt().getPrompt())) {
            processedPrompt = checkResp.getSysPrompt().getPrompt();
            log.info("检测到处理后的prompt，将用于替换所有message的content - prompt长度: {}", processedPrompt.length());
        }

        // 2. 转换messages为content格式（如果存在处理后的prompt，则使用prompt替换content）
        List<Map<String, Object>> content = convertMessagesToContent(messages, processedPrompt);

        // 3. 如果存在ragId，调用emoon匹配接口并添加知识库内容到content中
        // 使用checkResp中的ragId（已经从think_rag表查询到的ragid字段），而不是request中的ragId（id字段）
        String ragId = null;
        if (checkResp != null && StringUtils.isNotBlank(checkResp.getRagId())) {
            ragId = checkResp.getRagId(); // 使用从think_rag表查询到的ragid
            log.info("使用CheckResp中的ragId进行匹配，ragId: {}", ragId);
        } else if (StringUtils.isNotBlank(request.getRagId())) {
            ragId = request.getRagId(); // 如果没有checkResp中的ragId，则使用请求参数中的ragId
            log.warn("CheckResp中未找到ragId，使用请求参数中的ragId: {}", ragId);
        }

        if (StringUtils.isNotBlank(ragId)) {
            // 调用RAG匹配接口获取知识库内容
            List<Map<String, Object>> ragResults = processMatchingForRag(messages, ragId, model, processedPrompt);
            // 将RAG检索结果添加到content中
            if (!ragResults.isEmpty()) {
                String ragContent = extractRagContent(ragResults);
                if (StringUtils.isNotBlank(ragContent)) {
                    addRagContentToMessages(content, ragContent);
                    log.info("已将RAG检索结果添加到content中，知识库内容长度: {}", ragContent.length());
                }
            }
        }

        // 4. 组装参数并请求模型
        // 只使用checkResp中的modelUrl，如果为空则返回错误
        if (checkResp == null || StringUtils.isBlank(checkResp.getModelUrl())) {
            log.error("modelUrl为空，无法请求模型");
            try {
                emitter.send(SseEmitter.event()
                    .name("error")
                    .data("{\"error\":\"模型URL未配置，请联系管理员\"}"));
                emitter.complete();
            } catch (IOException e) {
                log.error("发送错误信息失败", e);
            }
            return;
        }

        String apiUrl = checkResp.getModelUrl();
        log.info("使用CheckResp中的modelUrl: {}", apiUrl);


        // 为 deepseek-chat 和 qwen3-32B 模型添加 chat_template_kwargs 参数，禁止返回思考过程
        Map<String, Object> chatTemplateKwargs = null;
        String currentModel = model != null ? model : "qwen3-32B";
        if ("deepseek-chat".equalsIgnoreCase(currentModel) || "qwen3-32B".equalsIgnoreCase(currentModel)) {
            chatTemplateKwargs = new HashMap<>();
            chatTemplateKwargs.put("enable_thinking", false);
            log.info("为模型 {} 添加 chat_template_kwargs 参数，禁止返回思考过程", currentModel);
        }

        Map<String, Object> requestData = buildXinferenceRequest(content, currentModel, stream, chatTemplateKwargs);

        // 如果指定了temperature，添加到请求数据中
        if (temperature != null) {
            requestData.put("temperature", temperature);
        }

        // 5. 调用流式API并返回结果
        // 对于deepseek-chat模型，需要传递API key
        String apiKey = null;
        if ("deepseek-chat".equalsIgnoreCase(model)) {
            apiKey = deepseekApiKey;
        }
        String reply = processSimpleStream(emitter, apiUrl, requestData, model, apiKey);

        // 6. 流式响应结束后，保存请求记录到数据库
        saveAskInfo(request, sign, reply);
    }

    /**
     * 简化的流式处理方法
     * @return 模型返回的完整内容
     */
    private String processSimpleStream(SseEmitter emitter, String apiUrl, Map<String, Object> requestData, String model, String apiKey) throws IOException {
        try {
            log.info("开始处理流式响应 - apiUrl: {}", apiUrl);
            URL url = new URL(apiUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            connection.setConnectTimeout(60000);
            connection.setReadTimeout(86400000);

            // 设置请求头
            connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
            connection.setRequestProperty("Accept", "text/event-stream");
            connection.setRequestProperty("Accept-Charset", "UTF-8");

            // 如果提供了API key，添加Authorization header（用于deepseek-chat等需要认证的模型）
            if (StringUtils.isNotBlank(apiKey)) {
                connection.setRequestProperty("Authorization", "Bearer " + apiKey);
                log.info("添加Authorization header for model: {}", model);
            }

            // 发送请求数据
            try (OutputStream os = connection.getOutputStream()) {
                String jsonData = JSONUtil.toJsonStr(requestData);
                log.info("发送请求数据: {}", jsonData);
                os.write(jsonData.getBytes(StandardCharsets.UTF_8));
            }

            // 检查HTTP响应码
            int responseCode = connection.getResponseCode();
            log.info("HTTP响应码: {}", responseCode);
            if (responseCode != HttpURLConnection.HTTP_OK) {
                // 读取错误响应
                try (BufferedReader errorReader = new BufferedReader(
                        new InputStreamReader(connection.getErrorStream(), StandardCharsets.UTF_8))) {
                    StringBuilder errorResponse = new StringBuilder();
                    String errorLine;
                    while ((errorLine = errorReader.readLine()) != null) {
                        errorResponse.append(errorLine);
                    }
                    log.error("API请求失败，响应码: {}, 错误信息: {}", responseCode, errorResponse.toString());
                    try {
                        emitter.send(SseEmitter.event()
                            .name("error")
                            .data("{\"error\":\"API请求失败，响应码: " + responseCode + ", 错误: " + errorResponse.toString() + "\"}"));
                    } catch (Exception ex) {
                        log.error("发送错误信息失败，emitter可能已关闭", ex);
                    }
                    emitter.complete();
                    return null;  // 返回null表示请求失败
                }
            }

            // 声明变量存储模型返回的完整内容（类似PHP的$reply）
            StringBuilder reply = new StringBuilder("SSE: ");

            // 读取流式响应
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    if (line.startsWith("data: ")) {
                        String data = line.substring(6).trim();
                        // 处理结束标记
                        if ("[DONE]".equals(data)) {
                            log.info("收到结束标记[DONE]");
                            log.info("模型返回的完整内容: {}", reply.toString());
                            try {
                                emitter.send(SseEmitter.event()
                                    .name("end")
                                    .data("{\"status\":\"done\"}"));
                            } catch (IllegalStateException e) {
                                log.warn("发送结束标记失败，emitter已关闭", e);
                            }
                            break;
                        }
                        // 跳过空数据
                        if (StringUtils.isBlank(data)) {
                            continue;
                        }
                        // 处理有效数据
                        boolean shouldBreak = false;
                        try {
                            JSONObject jsonData = JSONUtil.parseObj(data);
                            // 如果指定了model，更新model字段
                            if (StringUtils.isNotBlank(model)) {
                                jsonData.set("model", model);
                            }

                            // 根据模型名提取内容并拼接到reply中
                            String extractedContent = extractContentFromResponse(jsonData, model);
                            if (StringUtils.isNotBlank(extractedContent)) {
                                reply.append(extractedContent);
                            }

                            // 检查是否有finish_reason为stop的响应，表示流结束
                            JSONArray choicesArray = jsonData.getJSONArray("choices");
                            if (choicesArray != null && choicesArray.size() > 0) {
                                JSONObject choice = choicesArray.getJSONObject(0);
                                String finishReason = choice.getStr("finish_reason");
                                if ("stop".equals(finishReason)) {
                                    log.info("检测到流结束标记(finish_reason=stop)");
                                    log.info("模型返回的完整内容: {}", reply.toString());
                                    shouldBreak = true;
                                }
                            }

                            String jsonStr = JSONUtil.toJsonStr(jsonData);
                            // 发送SSE事件
                            try {
                                emitter.send(SseEmitter.event()
                                    .data(jsonStr));
                            } catch (IllegalStateException e) {
                                // emitter已关闭，停止读取
                                log.warn("emitter已关闭，停止读取流式数据");
                                break;
                            }

                            // 如果检测到流结束，跳出循环
                            if (shouldBreak) {
                                break;
                            }
                        } catch (Exception e) {
                            log.error("解析流式数据失败: {}, 原始数据: {}", e.getMessage(), data, e);
                        }
                    }
                }
            }
            // 正常读取完所有数据后，完成emitter
            try {
                emitter.complete();
            } catch (IllegalStateException e) {
                log.warn("emitter已完成，忽略重复完成操作", e);
            }
            // 返回完整的回复内容（去掉前缀"SSE: "）
            String finalReply = reply.toString();
            if (finalReply.startsWith("SSE: ")) {
                finalReply = finalReply.substring(5);
            }
            return finalReply;
        } catch (Exception e) {
            log.error("处理流式响应失败", e);
            try {
                emitter.send(SseEmitter.event()
                    .name("error")
                    .data("{\"error\":\"" + e.getMessage() + "\"}"));
            } catch (IllegalStateException ex) {
                log.warn("发送错误信息失败，emitter已关闭", ex);
            } finally {
                try {
                    emitter.complete();
                } catch (IllegalStateException ex) {
                    log.warn("完成emitter失败，可能已经关闭", ex);
                }
            }
            return null;
        }
    }

    /**
     * 保存请求记录到数据库
     * @param request SSE请求参数
     * @param sign 加密的签名
     * @param reply 模型返回的完整内容
     */
    private void saveAskInfo(SseRequest request, String sign, String reply) {
        try {
            // 将请求参数转换为JSON字符串
            String decrypt = JSONUtil.toJsonStr(request);

            // 获取当前时间戳（秒）
            long time = System.currentTimeMillis();

            // 构建AskInfoDo对象
            AskInfoDo askInfoDo = AskInfoDo.builder()
                    .token("")  // token设置为空字符串（数据库字段不允许null）
                    .decrypt(decrypt)  // 输入的参数存储到decrypt字段
                    .encryption(sign)  // 加密的sign存储到encryption字段
                    .reply(reply != null ? reply : "")  // 模型返回存储到reply字段
                    .time(time)  // 存储的时间戳存到time字段
                    .build();

            // 保存到数据库
            Integer result = askInfoService.insertAskInfo(askInfoDo);
            if (result != null && result > 0) {
                log.info("请求记录保存成功 - id: {}", askInfoDo.getId());
            } else {
                log.warn("请求记录保存失败");
            }
        } catch (Exception e) {
            log.error("保存请求记录失败", e);
        }
    }

    /**
     * 根据模型名从SSE响应中提取内容
     * 不同模型的数据结构可能不同，需要根据模型名分别处理
     *
     * @param jsonData SSE响应的JSON数据
     * @param model 模型名称
     * @return 提取的内容字符串，如果没有内容则返回null
     */
    private String extractContentFromResponse(JSONObject jsonData, String model) {
        if (jsonData == null) {
            return null;
        }

        try {
            // 根据模型名选择不同的提取方式
            if ("deepseek-chat".equalsIgnoreCase(model)) {
                // deepseek-chat模型：从choices[0].delta.content中提取
                JSONArray choicesArray = jsonData.getJSONArray("choices");
                if (choicesArray != null && choicesArray.size() > 0) {
                    JSONObject choice = choicesArray.getJSONObject(0);
                    JSONObject delta = choice.getJSONObject("delta");
                    if (delta != null) {
                        String content = delta.getStr("content");
                        return content != null ? content : "";
                    }
                }
            } else if ("qwen3-32B".equalsIgnoreCase(model) || "emoon-E1-13B".equalsIgnoreCase(model)) {
                // qwen3-32B或emoon-E1-13B模型：从choices[0].delta.content中提取
                JSONArray choicesArray = jsonData.getJSONArray("choices");
                if (choicesArray != null && choicesArray.size() > 0) {
                    JSONObject choice = choicesArray.getJSONObject(0);
                    JSONObject delta = choice.getJSONObject("delta");
                    if (delta != null) {
                        String content = delta.getStr("content");
                        return content != null ? content : "";
                    }
                }
            } else {
                // 其他模型：默认从choices[0].delta.content中提取
                JSONArray choicesArray = jsonData.getJSONArray("choices");
                if (choicesArray != null && choicesArray.size() > 0) {
                    JSONObject choice = choicesArray.getJSONObject(0);
                    JSONObject delta = choice.getJSONObject("delta");
                    if (delta != null) {
                        String content = delta.getStr("content");
                        return content != null ? content : "";
                    }
                }
            }
        } catch (Exception e) {
            log.warn("提取模型返回内容失败，模型: {}, 错误: {}", model, e.getMessage());
        }

        return null;
    }

    /**
     * 从messages中提取所有文本内容
     */
    private List<String> extractTextsFromMessages(List<Map<String, Object>> messages) {
        List<String> texts = new ArrayList<>();
        if (messages == null || messages.isEmpty()) {
            return texts;
        }

        for (Map<String, Object> message : messages) {
            Object contentObj = message.get("content");
            if (contentObj instanceof List) {
                List<Map<String, Object>> contentList = (List<Map<String, Object>>) contentObj;
                for (Map<String, Object> item : contentList) {
                    String type = (String) item.get("type");
                    if ("text".equals(type)) {
                        String text = (String) item.get("text");
                        if (StringUtils.isNotBlank(text)) {
                            texts.add(text);
                        }
                    }
                }
            } else if (contentObj instanceof String) {
                String text = (String) contentObj;
                if (StringUtils.isNotBlank(text)) {
                    texts.add(text);
                }
            }
        }
        return texts;
    }

    /**
     * 为RAG场景调用emoon匹配接口
     *
     * @param messages 消息列表
     * @param ragId RAG知识库ID（ragid字段）
     * @param model 模型名称（ruleCode）
     * @param processedPrompt 处理后的prompt（如果存在，将替换messages中的content）
     * @return 匹配结果列表
     */
    private List<Map<String, Object>> processMatchingForRag(List<Map<String, Object>> messages, String ragId, String model, String processedPrompt) {
        try {
            log.info("开始调用emoon匹配接口，ragId: {}, model: {}", ragId, model);

            // 如果存在处理后的prompt，使用prompt替换messages中的content
            String medicalContent;
            if (StringUtils.isNotBlank(processedPrompt)) {
                log.info("使用处理后的prompt作为RAG匹配内容 - prompt长度: {}", processedPrompt.length());
                medicalContent = processedPrompt;
            } else {
                // 从messages中提取文本内容
                log.info("从messages中提取文本内容，messages数量: {}", messages != null ? messages.size() : 0);
                List<String> textsToEmbed = extractTextsFromMessages(messages);
            log.info("提取到文本片段数量: {}, 文本片段内容: {}",
                    textsToEmbed.size(),
                    textsToEmbed.size() > 0 ? textsToEmbed.stream()
                            .map(text -> text.length() > 30 ? text.substring(0, 30) + "..." : text)
                            .collect(Collectors.joining(", ")) : "无");

                if (textsToEmbed.isEmpty()) {
                    log.warn("未找到需要匹配的文本内容，ragId: {}", ragId);
                    return Collections.emptyList();
                }

                // 合并所有文本内容作为医疗内容
                medicalContent = String.join(" ", textsToEmbed);
                log.info("合并后的医疗内容长度: {}, 内容预览: {}",
                        medicalContent.length(),
                        medicalContent.length() > 100 ? medicalContent.substring(0, 100) + "..." : medicalContent);
            }

            // 调用emoon匹配接口
            log.info("准备调用emoon匹配接口，参数 - medicalContent长度: {}, ruleCode(model): {}, kid(ragId): {}",
                    medicalContent.length(), model, ragId);
            List<Map<String, Object>> matchResults = callEmoonMatchingApi(medicalContent, model, ragId);

            if (!matchResults.isEmpty()) {
                log.info("emoon匹配完成，ragId: {}, 查询文本长度: {}, 匹配结果数: {}",
                        ragId, medicalContent.length(), matchResults.size());
                // 打印匹配结果的详细信息
                log.info("========== RAG匹配结果详情 ==========");
                for (int i = 0; i < matchResults.size(); i++) {
                    Map<String, Object> result = matchResults.get(i);
                    String resultJson = JSONUtil.toJsonStr(result);
                    log.info("匹配结果[{}]: {}", i + 1, resultJson.length() > 1000 ? resultJson.substring(0, 1000) + "..." : resultJson);
                }
                log.info("=====================================");
            } else {
                log.warn("未找到匹配的数据，ragId: {}, 查询文本长度: {}", ragId, medicalContent.length());
            }

            return matchResults;
        } catch (Exception e) {
            log.error("调用emoon匹配接口失败，ragId: {}", ragId, e);
            return Collections.emptyList();
        }
    }

    /**
     * 获取RAG认证Token
     *
     * @return Token字符串，如果获取失败返回null
     */
    private String getRagAuthToken() {
        try {
            log.info("开始获取RAG认证Token - username: {}, url: {}", emoonMatchingUsername, emoonMatchingUrl);

            // 构建登录请求URL（emoonMatchingUrl已包含/api前缀）
            String loginUrl = emoonMatchingUrl + "/auth/login";
            log.info("RAG登录接口URL: {}", loginUrl);

            // 构建登录请求数据
            Map<String, Object> loginData = new HashMap<>();
            loginData.put("username", emoonMatchingUsername);
            loginData.put("password", emoonMatchingPassword);

            String jsonData = JSONUtil.toJsonStr(loginData);
            log.info("RAG登录请求数据: {}", jsonData);

            // 发送HTTP POST请求
            URL requestUrl = new URL(loginUrl);
            HttpURLConnection connection = (HttpURLConnection) requestUrl.openConnection();
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            connection.setConnectTimeout(10000);
            connection.setReadTimeout(30000);
            connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
            connection.setRequestProperty("Accept", "application/json");
            connection.setRequestProperty("Cache-Control", "no-cache");
            connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");

            // 允许重定向
            connection.setInstanceFollowRedirects(true);

            // 发送请求数据
            try (OutputStream os = connection.getOutputStream()) {
                os.write(jsonData.getBytes(StandardCharsets.UTF_8));
                os.flush();
            }

            // 检查HTTP响应码
            int responseCode = connection.getResponseCode();
            log.info("RAG登录接口响应码: {}", responseCode);

            if (responseCode != HttpURLConnection.HTTP_OK) {
                // 读取错误响应
                InputStream errorStream = connection.getErrorStream();
                if (errorStream != null) {
                    try (BufferedReader errorReader = new BufferedReader(
                            new InputStreamReader(errorStream, StandardCharsets.UTF_8))) {
                        StringBuilder errorResponse = new StringBuilder();
                        String errorLine;
                        while ((errorLine = errorReader.readLine()) != null) {
                            errorResponse.append(errorLine);
                        }
                        log.error("RAG登录接口调用失败，响应码: {}, URL: {}, 错误信息: {}",
                                responseCode, loginUrl, errorResponse.toString());
                    }
                } else {
                    // 尝试读取正常响应流（某些服务器在错误时也可能使用正常流）
                    try (BufferedReader reader = new BufferedReader(
                            new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
                        StringBuilder response = new StringBuilder();
                        String line;
                        while ((line = reader.readLine()) != null) {
                            response.append(line);
                        }
                        log.error("RAG登录接口调用失败，响应码: {}, URL: {}, 响应内容: {}",
                                responseCode, loginUrl, response.toString());
                    } catch (Exception e) {
                        log.error("无法读取错误响应，URL: {}", loginUrl, e);
                    }
                }

                // 405 错误通常表示方法不允许，可能是路径或方法不对
                if (responseCode == 405) {
                    log.error("405 Not Allowed - 可能的原因：1) 路径不正确 2) HTTP方法不对 3) nginx配置问题。请检查接口路径: {}", loginUrl);
                }

                return null;
            }

            // 读取响应数据
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
                StringBuilder response = new StringBuilder();
                String line;
                while ((line = reader.readLine()) != null) {
                    response.append(line);
                }

                String responseStr = response.toString();
                log.debug("RAG登录接口响应数据: {}", responseStr);

                // 解析响应数据，提取token
                JSONObject responseJson = JSONUtil.parseObj(responseStr);
                if (responseJson.containsKey("data")) {
                    Object dataObj = responseJson.get("data");
                    if (dataObj instanceof Map) {
                        Map<String, Object> dataMap = (Map<String, Object>) dataObj;
                        String token = (String) dataMap.get("token");
                        if (StringUtils.isNotBlank(token)) {
                            log.info("RAG认证Token获取成功");
                            return token;
                        } else {
                            log.warn("RAG登录响应中未找到token字段");
                        }
                    }
                }
                log.warn("RAG登录响应格式不正确，无法提取token");
                return null;
            }

        } catch (Exception e) {
            log.error("获取RAG认证Token失败", e);
            return null;
        }
    }

    /**
     * 调用emoon匹配接口（RAG相似度匹配）
     *
     * @param medicalContent 医疗内容（用户输入）
     * @param ruleCode 规则代码（模型名称）
     * @param kid 知识库ID（ragid）
     * @return 匹配结果列表
     */
    private List<Map<String, Object>> callEmoonMatchingApi(String medicalContent, String ruleCode, String kid) {
        try {
            if (StringUtils.isBlank(medicalContent)) {
                log.warn("医疗内容为空，kid: {}", kid);
                return Collections.emptyList();
            }

            if (StringUtils.isBlank(kid)) {
                log.warn("kid为空，无法进行匹配");
                return Collections.emptyList();
            }

            // 如果ruleCode为空，使用默认值
            if (StringUtils.isBlank(ruleCode)) {
                ruleCode = "123"; // 根据实际需求设置默认值
            }

            log.info("调用emoon RAG匹配接口 - medicalContent长度: {}, model: {}, kid: {}",
                    medicalContent.length(), ruleCode, kid);

            // 1. 先获取认证Token
            String token = getRagAuthToken();
            if (StringUtils.isBlank(token)) {
                log.error("获取RAG认证Token失败，无法调用匹配接口");
                return Collections.emptyList();
            }

            // 2. 构建RAG匹配请求URL（emoonMatchingUrl已包含/api前缀）
            String url = emoonMatchingUrl + "/rag/schema";
            log.info("emoon RAG匹配接口URL: {}", url);

            // 3. 构建请求数据（按照PHP代码的格式）
            Map<String, Object> requestData = new HashMap<>();
            requestData.put("messages", medicalContent);  // 用户输入的文本内容
            requestData.put("model", ruleCode);  // 模型名称
            requestData.put("maxDistance", emoonMatchingMaxDistance);  // 相似度匹配的最大距离
            requestData.put("kid", kid);  // 知识库ID

            log.info("参数组装完成 - messages长度: {}, model: '{}', maxDistance: {}, kid: '{}'",
                    medicalContent != null ? medicalContent.length() : 0, ruleCode, emoonMatchingMaxDistance, kid);

            String jsonData = JSONUtil.toJsonStr(requestData);
            log.info("emoon RAG匹配接口请求数据(JSON): {}", jsonData.length() > 500 ? jsonData.substring(0, 500) + "..." : jsonData);

            // 4. 发送HTTP POST请求
            URL requestUrl = new URL(url);
            HttpURLConnection connection = (HttpURLConnection) requestUrl.openConnection();
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            connection.setConnectTimeout(10000);
            connection.setReadTimeout(30000);
            connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
            connection.setRequestProperty("Accept", "application/json");
            connection.setRequestProperty("Authorization", "Bearer " + token);  // 添加认证头
            connection.setRequestProperty("Cache-Control", "no-cache");
            connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");

            // 发送请求数据
            try (OutputStream os = connection.getOutputStream()) {
                os.write(jsonData.getBytes(StandardCharsets.UTF_8));
            }

            // 检查HTTP响应码
            int responseCode = connection.getResponseCode();
            log.debug("emoon RAG匹配接口响应码: {}", responseCode);

            if (responseCode != HttpURLConnection.HTTP_OK) {
                // 读取错误响应
                try (BufferedReader errorReader = new BufferedReader(
                        new InputStreamReader(connection.getErrorStream(), StandardCharsets.UTF_8))) {
                    StringBuilder errorResponse = new StringBuilder();
                    String errorLine;
                    while ((errorLine = errorReader.readLine()) != null) {
                        errorResponse.append(errorLine);
                    }
                    log.error("emoon RAG匹配接口调用失败，响应码: {}, 错误信息: {}", responseCode, errorResponse.toString());
                }
                return Collections.emptyList();
            }

            // 读取响应数据
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
                StringBuilder response = new StringBuilder();
                String line;
                while ((line = reader.readLine()) != null) {
                    response.append(line);
                }

                String responseStr = response.toString();
                log.info("emoon RAG匹配接口原始响应数据: {}", responseStr.length() > 2000 ? responseStr.substring(0, 2000) + "..." : responseStr);

                // 解析响应数据
                // RAG接口可能返回：{"prompt": "...", "vector": [...], "nearest": [...]} 或其他格式
                List<Map<String, Object>> results = new ArrayList<>();

                try {
                    JSONObject responseJson = JSONUtil.parseObj(responseStr);
                    // 如果响应是对象格式，直接添加
                    if (responseJson != null && !responseJson.isEmpty()) {
                        results.add(responseJson);
                        log.info("RAG响应解析成功（对象格式），包含字段: {}", responseJson.keySet());
                    }
                } catch (Exception e) {
                    // 如果解析失败，尝试解析为数组
                    try {
                        JSONArray jsonArray = JSONUtil.parseArray(responseStr);
                        log.info("RAG响应解析成功（数组格式），数组长度: {}", jsonArray.size());
                        for (int i = 0; i < jsonArray.size(); i++) {
                            Object item = jsonArray.get(i);
                            if (item instanceof Map) {
                                results.add((Map<String, Object>) item);
                            }
                        }
                    } catch (Exception ex) {
                        log.warn("解析RAG响应数据失败: {}", ex.getMessage());
                    }
                }

                log.info("emoon RAG匹配接口调用成功，kid: {}, 匹配结果数: {}", kid, results.size());

                // 打印解析后的匹配结果
                if (!results.isEmpty()) {
                    log.info("========== RAG匹配接口返回结果 ==========");
                    for (int i = 0; i < results.size(); i++) {
                        Map<String, Object> result = results.get(i);
                        String resultJson = JSONUtil.toJsonStr(result);
                        log.info("结果[{}] (kid: {}): {}", i + 1, kid, resultJson.length() > 2000 ? resultJson.substring(0, 2000) + "..." : resultJson);
                    }
                    log.info("=========================================");
                }

                return results;
            }

        } catch (Exception e) {
            log.error("调用emoon RAG匹配接口失败，kid: {}, medicalContent: {}",
                    kid, medicalContent != null && medicalContent.length() > 50 ? medicalContent.substring(0, 50) + "..." : medicalContent, e);
            return Collections.emptyList();
        }
    }

    /**
     * 将SseRequest的messages转换为content格式
     * @param messages 消息列表
     * @param processedPrompt 处理后的prompt（如果存在，将替换所有message的content）
     */
    private List<Map<String, Object>> convertMessagesToContent(List<Map<String, Object>> messages, String processedPrompt) {
        List<Map<String, Object>> content = new ArrayList<>();

        if (messages == null || messages.isEmpty()) {
            return content;
        }

        for (Map<String, Object> message : messages) {
            String role = (String) message.get("role");
            Object contentObj = message.get("content");

            Map<String, Object> contentItem = new HashMap<>();
            contentItem.put("role", role);

            // 如果存在处理后的prompt，直接使用prompt作为content
            if (StringUtils.isNotBlank(processedPrompt)) {
                contentItem.put("content", processedPrompt);
                log.debug("使用处理后的prompt替换message的content - role: {}, prompt长度: {}", role, processedPrompt.length());
            } else if (contentObj instanceof List) {
                // content是数组，需要提取文本内容
                List<Map<String, Object>> contentList = (List<Map<String, Object>>) contentObj;
                StringBuilder textContent = new StringBuilder();

                for (Map<String, Object> item : contentList) {
                    String type = (String) item.get("type");
                    if ("text".equals(type)) {
                        String text = (String) item.get("text");
                        if (StringUtils.isNotBlank(text)) {
                            if (textContent.length() > 0) {
                                textContent.append("\n");
                            }
                            textContent.append(text);
                        }
                    }
                    // image和video类型暂时忽略，或可以根据需要处理
                }

                contentItem.put("content", textContent.toString());
            } else if (contentObj instanceof String) {
                // content是字符串
                contentItem.put("content", contentObj);
            } else {
                // 如果content为空或未知类型，设置为空字符串
                contentItem.put("content", "");
            }

            content.add(contentItem);
        }

        return content;
    }

    /**
     * 从RAG检索结果中提取知识库内容
     * RAG接口可能返回：{"prompt": "...", "vector": [...], "nearest": [...]} 或其他格式
     *
     * @param ragResults RAG检索结果列表
     * @return 提取的知识库内容字符串
     */
    private String extractRagContent(List<Map<String, Object>> ragResults) {
        if (ragResults == null || ragResults.isEmpty()) {
            return null;
        }

        StringBuilder ragContent = new StringBuilder();

        for (Map<String, Object> result : ragResults) {
            if (result == null || result.isEmpty()) {
                continue;
            }

            // 优先提取prompt字段
            if (result.containsKey("prompt")) {
                Object promptObj = result.get("prompt");
                if (promptObj != null) {
                    String promptStr = promptObj.toString();
                    if (StringUtils.isNotBlank(promptStr)) {
                        if (ragContent.length() > 0) {
                            ragContent.append("\n\n");
                        }
                        ragContent.append(promptStr);
                    }
                }
            }

            // 如果有nearest字段（最近邻结果），也提取
            if (result.containsKey("nearest")) {
                Object nearestObj = result.get("nearest");
                if (nearestObj instanceof List) {
                    List<?> nearestList = (List<?>) nearestObj;
                    for (Object item : nearestList) {
                        if (item instanceof Map) {
                            Map<String, Object> itemMap = (Map<String, Object>) item;
                            // 尝试提取content、text、data等字段
                            for (String key : new String[]{"content", "text", "data", "chunk"}) {
                                if (itemMap.containsKey(key)) {
                                    Object value = itemMap.get(key);
                                    if (value != null) {
                                        String valueStr = value.toString();
                                        if (StringUtils.isNotBlank(valueStr)) {
                                            if (ragContent.length() > 0) {
                                                ragContent.append("\n\n");
                                            }
                                            ragContent.append(valueStr);
                                            break; // 找到一个字段就跳出
                                        }
                                    }
                                }
                            }
                        } else if (item != null) {
                            String itemStr = item.toString();
                            if (StringUtils.isNotBlank(itemStr)) {
                                if (ragContent.length() > 0) {
                                    ragContent.append("\n\n");
                                }
                                ragContent.append(itemStr);
                            }
                        }
                    }
                }
            }
        }

        return ragContent.length() > 0 ? ragContent.toString() : null;
    }

    /**
     * 将RAG检索的知识库内容添加到content中
     * 通常添加到user消息的content前面，作为上下文信息
     *
     * @param content 消息content列表
     * @param ragContent RAG检索的知识库内容
     */
    private void addRagContentToMessages(List<Map<String, Object>> content, String ragContent) {
        if (content == null || content.isEmpty() || StringUtils.isBlank(ragContent)) {
            return;
        }

        // 查找user角色的消息，将知识库内容添加到其content前面
        for (Map<String, Object> contentItem : content) {
            String role = (String) contentItem.get("role");
            if ("user".equalsIgnoreCase(role)) {
                String originalContent = (String) contentItem.get("content");
                // 将知识库内容添加到原始内容前面
                String newContent = "【知识库检索结果】\n" + ragContent + "\n\n【用户问题】\n" +
                        (StringUtils.isNotBlank(originalContent) ? originalContent : "");
                contentItem.put("content", newContent);
                log.info("已将RAG知识库内容添加到user消息中，原始长度: {}, 新增长度: {}, 总长度: {}",
                        originalContent != null ? originalContent.length() : 0,
                        ragContent.length(),
                        newContent.length());
                break; // 只处理第一个user消息
            }
        }
    }

    /**
     * 构建xinference请求数据
     */
    private Map<String, Object> buildXinferenceRequest(List<Map<String, Object>> content,
                                                       String model, boolean stream,
                                                       Map<String, Object> chatTemplateKwargs) {
        List<Map<String, Object>> messages = new ArrayList<>();
        Map<String, Object> systemMsg = new HashMap<>();
        systemMsg.put("role", "system");

        // 根据模型类型设置不同的system消息，设置content中前一段提示词。
        systemMsg.put("content", "你是一个人工智能助手。");

        messages.add(systemMsg);
        messages.addAll(content);

        Map<String, Object> data = new HashMap<>();
        data.put("messages", messages);
        data.put("model", model);
        data.put("stream", stream);

        // 设置 temperature、top_p 和 max_tokens（无论是否有 chat_template_kwargs）
        data.put("temperature", 0.1);
        data.put("top_p", 0.9);
        // 根据模型类型设置不同的max_tokens
        // deepseek-chat的max_tokens范围是[1, 8192]
        if ("deepseek-chat".equalsIgnoreCase(model)) {
            data.put("max_tokens", 8192);
        } else {
            data.put("max_tokens", 32768);
        }

        // 如果提供了 chat_template_kwargs，添加到请求数据中
        if (chatTemplateKwargs != null) {
            data.put("chat_template_kwargs", JSONUtil.toJsonStr(chatTemplateKwargs));
        }

        return data;
    }

    /**
     * 生成签名（内部方法）
     * 1. 将 requestId、timestamp、payload、privateKey 这四个参数按 ASCII 码升序排列
     * 2. 将排序后的参数按 kv 格式拼接成字符串
     * 3. 对拼接后的字符串进行 MD5 加密，得到 sign
     *
     * @param requestId 请求ID
     * @param timestamp 时间戳
     * @param payload 业务负载数据
     * @param privateKey 私钥
     * @return MD5加密后的签名
     */
    private String generateSignInternal(String requestId, String timestamp, String payload, String privateKey) {
        // 1. 按照固定顺序拼接：RequestId&Timestamp&payload&privateKey
        StringBuilder sb = new StringBuilder();
        sb.append(requestId != null ? requestId : "");
        sb.append("&");
        sb.append(timestamp != null ? timestamp : "");
        sb.append("&");
        sb.append(payload != null ? payload : "");
        sb.append("&");
        sb.append(privateKey != null ? privateKey : "");

        String signString = sb.toString();
        log.info("签名原始字符串: {}", signString);

        // 3. 对拼接后的字符串进行 MD5 加密
        // MD5 md5 = new MD5();
        // String sign = HexUtil.encodeHexStr(md5.digest(signString, CharsetUtil.UTF_8));
        // log.info("生成的签名: {}", sign);

        // return sign;

         try {
            // MD5哈希计算（UTF-8编码）
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] md5Bytes = md.digest(signString.getBytes(StandardCharsets.UTF_8));
            // 转为32位小写字符串
            String sign = new BigInteger(1, md5Bytes).toString(16);
            while (sign.length() < 32) {
                sign = "0" + sign; // 补全32位
            }
            sign = sign.toLowerCase();
            log.info("生成的签名: {}", sign);
            return sign;
        } catch (Exception e) {
            log.error("签名生成失败", e);
            throw new RuntimeException("签名生成失败", e);
        }
    }

    /**
     * 生成签名（HTTP接口）
     * 1. 将 requestId、timestamp、payload、privateKey 这四个参数按 ASCII 码升序排列
     * 2. 将排序后的参数按 kv 格式拼接成字符串
     * 3. 对拼接后的字符串进行 MD5 加密，得到 sign
     * @param request 签名请求参数
     * @return MD5加密后的签名
     */
    @PostMapping(value = "/generateSign")
    public R<String> generateSign(@RequestBody SignRequest request) {
        // 调用 MD5SignGenerateExample 打印调试信息

        String sign = generateSignInternal(
                request.getRequestId(),
                request.getTimestamp(),
                request.getPayload(),
                request.getPrivateKey()
        );
        return R.ok(sign);
    }

    /**
     * 上传音频文件并转换为Base64格式
     *
     * @param file 音频文件（MultipartFile）
     * @param mimeType 可选的MIME类型，如果不提供则根据文件扩展名自动检测
     * @return Base64编码的字符串，包含base64字段和可选的base64WithDataUri字段
     */
    @PostMapping(value = "/audioToBase64", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public R<Map<String, Object>> audioToBase64(
            @RequestParam("file") MultipartFile file,
            @RequestParam(value = "mimeType", required = false) String mimeType) {

        try {
            // 验证文件是否为空
            if (file == null || file.isEmpty()) {
                return R.fail(SystemEnum.ILLEGAL_REQUEST.getCode(), "音频文件不能为空");
            }

            // 验证文件大小（可选，例如限制为50MB）
            long maxSize = 50 * 1024 * 1024; // 50MB
            if (file.getSize() > maxSize) {
                return R.fail(SystemEnum.ILLEGAL_REQUEST.getCode(),
                        "音频文件大小不能超过50MB，当前大小: " + (file.getSize() / 1024 / 1024) + "MB");
            }

            log.info("接收音频文件上传 - 文件名: {}, 文件大小: {} bytes, ContentType: {}",
                    file.getOriginalFilename(), file.getSize(), file.getContentType());

            // 将MultipartFile转换为Base64
            String base64 = null;
            String base64WithDataUri = null;

            try {
                // 方式1：使用InputStream转换
                base64 = audioFileToBase64(file.getInputStream());

                if (base64 != null) {
                    // 如果没有指定MIME类型，使用文件原始ContentType或根据文件名检测
                    String detectedMimeType = mimeType;
                    if (StringUtils.isBlank(detectedMimeType)) {
                        detectedMimeType = file.getContentType();
                        if (StringUtils.isBlank(detectedMimeType) && StringUtils.isNotBlank(file.getOriginalFilename())) {
                            detectedMimeType = detectAudioMimeType(file.getOriginalFilename());
                        }
                    }

                    // 生成带data URI前缀的Base64
                    if (StringUtils.isNotBlank(detectedMimeType)) {
                        base64WithDataUri = "data:" + detectedMimeType + ";base64," + base64;
                    }

                    log.info("音频文件转换为Base64成功 - 文件名: {}, Base64长度: {}",
                            file.getOriginalFilename(), base64.length());
                } else {
                    return R.fail(SystemEnum.ILLEGAL_REQUEST.getCode(), "音频文件转换为Base64失败");
                }
            } catch (IOException e) {
                log.error("读取音频文件失败", e);
                return R.fail(SystemEnum.ILLEGAL_REQUEST.getCode(), "读取音频文件失败: " + e.getMessage());
            }

            // 构建返回结果
            Map<String, Object> result = new HashMap<>();
            result.put("base64", base64);
            result.put("base64Length", base64.length());
            result.put("fileName", file.getOriginalFilename());
            result.put("fileSize", file.getSize());
            result.put("contentType", file.getContentType());

            if (StringUtils.isNotBlank(base64WithDataUri)) {
                result.put("base64WithDataUri", base64WithDataUri);
            }

            return R.ok(result);

        } catch (Exception e) {
            log.error("处理音频文件上传失败", e);
            return R.fail(SystemEnum.ILLEGAL_REQUEST.getCode(), "处理音频文件失败: " + e.getMessage());
        }
    }

    /**
     * 将音频文件转换为Base64格式
     *
     * @param filePath 音频文件路径
     * @return Base64编码的字符串，如果转换失败返回null
     */
    public String audioFileToBase64(String filePath) {
        if (StringUtils.isBlank(filePath)) {
            log.warn("文件路径为空");
            return null;
        }
        try {
            File file = new File(filePath);
            return audioFileToBase64(file);
        } catch (Exception e) {
            log.error("转换音频文件为Base64失败，文件路径: {}", filePath, e);
            return null;
        }
    }

    /**
     * 将音频文件转换为Base64格式
     *
     * @param file 音频文件对象
     * @return Base64编码的字符串，如果转换失败返回null
     */
    public String audioFileToBase64(File file) {
        if (file == null || !file.exists()) {
            log.warn("文件不存在或为null，文件路径: {}", file != null ? file.getAbsolutePath() : "null");
            return null;
        }
        if (!file.isFile()) {
            log.warn("不是有效的文件，文件路径: {}", file.getAbsolutePath());
            return null;
        }
        try {
            byte[] fileBytes = Files.readAllBytes(file.toPath());
            return audioFileToBase64(fileBytes);
        } catch (IOException e) {
            log.error("读取音频文件失败，文件路径: {}", file.getAbsolutePath(), e);
            return null;
        }
    }

    /**
     * 将音频文件输入流转换为Base64格式
     *
     * @param inputStream 音频文件输入流
     * @return Base64编码的字符串，如果转换失败返回null
     */
    public String audioFileToBase64(InputStream inputStream) {
        if (inputStream == null) {
            log.warn("输入流为null");
            return null;
        }
        try {
            byte[] fileBytes = inputStream.readAllBytes();
            return audioFileToBase64(fileBytes);
        } catch (IOException e) {
            log.error("读取音频文件输入流失败", e);
            return null;
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                log.warn("关闭输入流失败", e);
            }
        }
    }

    /**
     * 将音频文件字节数组转换为Base64格式
     *
     * @param fileBytes 音频文件字节数组
     * @return Base64编码的字符串，如果转换失败返回null
     */
    public String audioFileToBase64(byte[] fileBytes) {
        if (fileBytes == null || fileBytes.length == 0) {
            log.warn("文件字节数组为空");
            return null;
        }
        try {
            // 使用hutool的Base64编码，不使用URL安全模式
            String base64 = Base64.encode(fileBytes);
            log.debug("音频文件转换为Base64成功，文件大小: {} bytes, Base64长度: {}", fileBytes.length, base64.length());
            return base64;
        } catch (Exception e) {
            log.error("转换音频文件字节数组为Base64失败，文件大小: {} bytes", fileBytes != null ? fileBytes.length : 0, e);
            return null;
        }
    }

    /**
     * 将音频文件转换为Base64格式（带data URI前缀）
     *
     * @param filePath 音频文件路径
     * @param mimeType MIME类型，如 "audio/mpeg", "audio/wav", "audio/mp3" 等，如果为null则自动检测
     * @return 带data URI前缀的Base64编码字符串，格式：data:audio/mpeg;base64,xxx...
     */
    public String audioFileToBase64WithDataUri(String filePath, String mimeType) {
        String base64 = audioFileToBase64(filePath);
        if (base64 == null) {
            return null;
        }

        // 如果没有指定MIME类型，根据文件扩展名自动检测
        if (StringUtils.isBlank(mimeType)) {
            mimeType = detectAudioMimeType(filePath);
        }

        return "data:" + mimeType + ";base64," + base64;
    }

    /**
     * 根据文件扩展名检测音频MIME类型
     *
     * @param filePath 文件路径
     * @return MIME类型字符串
     */
    private String detectAudioMimeType(String filePath) {
        if (StringUtils.isBlank(filePath)) {
            return "audio/mpeg"; // 默认MIME类型
        }

        String lowerPath = filePath.toLowerCase();
        if (lowerPath.endsWith(".mp3")) {
            return "audio/mpeg";
        } else if (lowerPath.endsWith(".wav")) {
            return "audio/wav";
        } else if (lowerPath.endsWith(".m4a")) {
            return "audio/mp4";
        } else if (lowerPath.endsWith(".ogg")) {
            return "audio/ogg";
        } else if (lowerPath.endsWith(".flac")) {
            return "audio/flac";
        } else if (lowerPath.endsWith(".aac")) {
            return "audio/aac";
        } else if (lowerPath.endsWith(".amr")) {
            return "audio/amr";
        } else {
            return "audio/mpeg"; // 默认MIME类型
        }
    }

    /**
     * 处理音频模型请求
     *
     * @param request SSE请求参数
     * @param checkResp 检查响应，包含modelUrl等信息
     * @return 音频转文字的结果
     */
    private R<Map<String, Object>> handleAudioModelRequest(SseRequest request, CheckResp checkResp) {
        try {
            // 1. 验证modelUrl是否存在
            if (checkResp == null || StringUtils.isBlank(checkResp.getModelUrl())) {
                log.error("音频模型请求失败 - modelUrl为空");
                return R.fail(SystemEnum.ILLEGAL_REQUEST.getCode(), "模型URL未配置，请联系管理员");
            }

            String modelUrl = checkResp.getModelUrl();
            String model = request.getModel();

            // 将chat/completions端点转换为audio/transcriptions端点
            String audioApiUrl = convertToAudioTranscriptionUrl(modelUrl);
            log.info("处理音频模型请求 - model: {}, 原始modelUrl: {}, 转换后audioApiUrl: {}",
                    model, modelUrl, audioApiUrl);

            // 2. 根据modelInType从messages中提取对应类型的数据
            String modelInType = checkResp.getModelInType();
            log.info("根据modelInType提取数据 - modelInType: {}", modelInType);

            // 从messages中提取数据和格式信息（根据modelInType提取对应type的数据）
            AudioExtractResult audioResult = extractAudioFromMessages(request.getMessages(), modelInType);
            if (audioResult == null || audioResult.getAudioBytes() == null || audioResult.getAudioBytes().length == 0) {
                log.error("音频模型请求失败 - 未找到音频数据");
                return R.fail(SystemEnum.ILLEGAL_REQUEST.getCode(), "请求中未找到音频数据");
            }

            log.info("提取到音频数据，大小: {} bytes, 格式: {}, MIME类型: {}",
                    audioResult.getAudioBytes().length,
                    audioResult.getFormat(),
                    audioResult.getMimeType());

            // 3. 调用音频模型API
            Map<String, Object> result = callAudioModelApi(
                    audioApiUrl,
                    model,
                    audioResult.getAudioBytes(),
                    audioResult.getFormat(),
                    audioResult.getMimeType()
            );

            return R.ok(result);
        } catch (Exception e) {
            log.error("处理音频模型请求异常", e);
            return R.fail(SystemEnum.ILLEGAL_REQUEST.getCode(), "处理音频模型请求失败: " + e.getMessage());
        }
    }

    /**
     * 音频提取结果类
     */
    private static class AudioExtractResult {
        private byte[] audioBytes;
        private String format;  // 如 "mp3", "wav" 等
        private String mimeType;  // 如 "audio/mpeg", "audio/wav" 等

        public AudioExtractResult(byte[] audioBytes, String format, String mimeType) {
            this.audioBytes = audioBytes;
            this.format = format;
            this.mimeType = mimeType;
        }

        public byte[] getAudioBytes() {
            return audioBytes;
        }

        public String getFormat() {
            return format;
        }

        public String getMimeType() {
            return mimeType;
        }
    }

    /**
     * 从messages中提取指定类型的数据（根据modelInType提取对应type的数据）
     *
     * @param messages 消息列表
     * @param modelInType 模型输入类型（如 "audio", "text", "image" 等），用于从content中提取对应type的数据
     * @return 音频提取结果，包含字节数组、格式和MIME类型，如果未找到则返回null
     */
    private AudioExtractResult extractAudioFromMessages(List<Map<String, Object>> messages, String modelInType) {
        if (messages == null || messages.isEmpty()) {
            return null;
        }

        // 根据modelInType确定要提取的type值
        String targetType = modelInType != null ? modelInType.toLowerCase() : "audio";
        log.info("根据modelInType提取数据 - modelInType: {}, 目标type: {}", modelInType, targetType);

        for (Map<String, Object> message : messages) {
            Object contentObj = message.get("content");
            if (contentObj instanceof List) {
                List<Map<String, Object>> contentList = (List<Map<String, Object>>) contentObj;
                for (Map<String, Object> item : contentList) {
                    String type = (String) item.get("type");
                    if (targetType.equalsIgnoreCase(type)) {
                        // 提取media_base64
                        String mediaBase64 = (String) item.get("media_base64");
                        if (StringUtils.isNotBlank(mediaBase64)) {
                            try {
                                // 提取格式信息
                                String mediaFormat = (String) item.get("media_format");
                                String mimeType = null;
                                String format = "mp3"; // 默认格式

                                // 处理data URI格式：data:audio/mpeg;base64,xxx...
                                String base64Data = mediaBase64;
                                if (mediaBase64.startsWith("data:")) {
                                    int commaIndex = mediaBase64.indexOf(',');
                                    if (commaIndex > 0) {
                                        String dataUriPrefix = mediaBase64.substring(0, commaIndex);
                                        // 提取MIME类型：data:audio/mpeg;base64
                                        int semicolonIndex = dataUriPrefix.indexOf(';');
                                        if (semicolonIndex > 0) {
                                            mimeType = dataUriPrefix.substring(5, semicolonIndex); // 跳过 "data:"
                                        } else {
                                            mimeType = dataUriPrefix.substring(5); // 跳过 "data:"
                                        }
                                        base64Data = mediaBase64.substring(commaIndex + 1);
                                    }
                                }

                                // 根据media_format或MIME类型确定格式
                                if (StringUtils.isNotBlank(mediaFormat)) {
                                    format = mediaFormat.toLowerCase();
                                    if (mimeType == null) {
                                        mimeType = getMimeTypeFromFormat(format);
                                    }
                                } else if (mimeType != null) {
                                    format = getFormatFromMimeType(mimeType);
                                } else {
                                    mimeType = "audio/mpeg"; // 默认MIME类型
                                }

                                // 解码base64
                                byte[] audioBytes = Base64.decode(base64Data);
                                log.info("成功提取音频数据，base64长度: {}, 解码后大小: {} bytes, 格式: {}, MIME类型: {}",
                                        base64Data.length(), audioBytes.length, format, mimeType);
                                return new AudioExtractResult(audioBytes, format, mimeType);
                            } catch (Exception e) {
                                log.error("解码音频base64数据失败", e);
                                return null;
                            }
                        }
                    }
                }
            }
        }

        log.warn("未在messages中找到type为audio的media_base64数据");
        return null;
    }

    /**
     * 根据格式获取MIME类型
     */
    private String getMimeTypeFromFormat(String format) {
        if (format == null) {
            return "audio/mpeg";
        }
        String lowerFormat = format.toLowerCase();
        switch (lowerFormat) {
            case "mp3":
                return "audio/mpeg";
            case "wav":
                return "audio/wav";
            case "m4a":
            case "mp4":
                return "audio/mp4";
            case "ogg":
                return "audio/ogg";
            case "flac":
                return "audio/flac";
            case "aac":
                return "audio/aac";
            case "amr":
                return "audio/amr";
            default:
                return "audio/mpeg";
        }
    }

    /**
     * 根据MIME类型获取格式
     */
    private String getFormatFromMimeType(String mimeType) {
        if (mimeType == null) {
            return "mp3";
        }
        String lowerMimeType = mimeType.toLowerCase();
        if (lowerMimeType.contains("mpeg") || lowerMimeType.contains("mp3")) {
            return "mp3";
        } else if (lowerMimeType.contains("wav")) {
            return "wav";
        } else if (lowerMimeType.contains("mp4") || lowerMimeType.contains("m4a")) {
            return "m4a";
        } else if (lowerMimeType.contains("ogg")) {
            return "ogg";
        } else if (lowerMimeType.contains("flac")) {
            return "flac";
        } else if (lowerMimeType.contains("aac")) {
            return "aac";
        } else if (lowerMimeType.contains("amr")) {
            return "amr";
        } else {
            return "mp3";
        }
    }

    /**
     * 调用音频模型API（multipart/form-data）
     *
     * @param apiUrl 模型API地址
     * @param model 模型名称
     * @param audioBytes 音频文件字节数组
     * @param format 音频格式（如 "mp3", "wav"）
     * @param mimeType MIME类型（如 "audio/mpeg", "audio/wav"）
     * @return API响应结果
     */
    private Map<String, Object> callAudioModelApi(String apiUrl, String model, byte[] audioBytes,
                                                   String format, String mimeType) throws IOException {
        log.info("调用音频模型API - apiUrl: {}, model: {}, audioSize: {} bytes, format: {}, mimeType: {}",
                apiUrl, model, audioBytes.length, format, mimeType);

        URL url = new URL(apiUrl);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("POST");
        connection.setDoOutput(true);
        connection.setConnectTimeout(60000);
        connection.setReadTimeout(300000); // 5分钟超时

        // 生成boundary用于multipart/form-data
        String boundary = "----WebKitFormBoundary" + System.currentTimeMillis();
        connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);

        // 确定文件名和Content-Type
        String filename = "audio." + (format != null ? format : "mp3");
        String contentType = mimeType != null ? mimeType : "audio/mpeg";

        try (OutputStream os = connection.getOutputStream()) {
            String lineEnd = "\r\n";
            String twoHyphens = "--";

            // 写入file字段
            os.write((twoHyphens + boundary + lineEnd).getBytes(StandardCharsets.UTF_8));
            os.write(("Content-Disposition: form-data; name=\"file\"; filename=\"" + filename + "\"" + lineEnd).getBytes(StandardCharsets.UTF_8));
            os.write(("Content-Type: " + contentType + lineEnd).getBytes(StandardCharsets.UTF_8));
            os.write(lineEnd.getBytes(StandardCharsets.UTF_8));

            // 写入音频文件数据（二进制）
            os.write(audioBytes);

            // 写入换行符
            os.write(lineEnd.getBytes(StandardCharsets.UTF_8));

            // 写入model字段
            os.write((twoHyphens + boundary + lineEnd).getBytes(StandardCharsets.UTF_8));
            os.write(("Content-Disposition: form-data; name=\"model\"" + lineEnd).getBytes(StandardCharsets.UTF_8));
            os.write(lineEnd.getBytes(StandardCharsets.UTF_8));
            os.write((model != null ? model : "whisper-large-v3").getBytes(StandardCharsets.UTF_8));
            os.write(lineEnd.getBytes(StandardCharsets.UTF_8));

            // 写入language字段
            os.write((twoHyphens + boundary + lineEnd).getBytes(StandardCharsets.UTF_8));
            os.write(("Content-Disposition: form-data; name=\"language\"" + lineEnd).getBytes(StandardCharsets.UTF_8));
            os.write(lineEnd.getBytes(StandardCharsets.UTF_8));
            os.write("chinese".getBytes(StandardCharsets.UTF_8));
            os.write(lineEnd.getBytes(StandardCharsets.UTF_8));

            // 写入prompt字段
            os.write((twoHyphens + boundary + lineEnd).getBytes(StandardCharsets.UTF_8));
            os.write(("Content-Disposition: form-data; name=\"prompt\"" + lineEnd).getBytes(StandardCharsets.UTF_8));
            os.write(lineEnd.getBytes(StandardCharsets.UTF_8));
            os.write("请将语音转换为简体中文。使用现代标准汉语，避免繁体字".getBytes(StandardCharsets.UTF_8));
            os.write(lineEnd.getBytes(StandardCharsets.UTF_8));

            // 写入response_format字段
            os.write((twoHyphens + boundary + lineEnd).getBytes(StandardCharsets.UTF_8));
            os.write(("Content-Disposition: form-data; name=\"response_format\"" + lineEnd).getBytes(StandardCharsets.UTF_8));
            os.write(lineEnd.getBytes(StandardCharsets.UTF_8));
            os.write("json".getBytes(StandardCharsets.UTF_8));
            os.write(lineEnd.getBytes(StandardCharsets.UTF_8));

            // 写入temperature字段
            os.write((twoHyphens + boundary + lineEnd).getBytes(StandardCharsets.UTF_8));
            os.write(("Content-Disposition: form-data; name=\"temperature\"" + lineEnd).getBytes(StandardCharsets.UTF_8));
            os.write(lineEnd.getBytes(StandardCharsets.UTF_8));
            os.write("0".getBytes(StandardCharsets.UTF_8));
            os.write(lineEnd.getBytes(StandardCharsets.UTF_8));

            // 结束boundary
            os.write((twoHyphens + boundary + twoHyphens + lineEnd).getBytes(StandardCharsets.UTF_8));
            os.flush();
        }

        // 读取响应
        int responseCode = connection.getResponseCode();
        log.info("音频模型API响应码: {}", responseCode);

        if (responseCode == HttpURLConnection.HTTP_OK) {
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
                StringBuilder response = new StringBuilder();
                String line;
                while ((line = reader.readLine()) != null) {
                    response.append(line);
                }

                String responseStr = response.toString();
                log.info("音频模型API响应: {}", responseStr);

                // 解析JSON响应
                JSONObject jsonResponse = JSONUtil.parseObj(responseStr);
                Map<String, Object> result = new HashMap<>();
                result.putAll(jsonResponse);
                return result;
            }
        } else {
            // 读取错误响应（使用更安全的方式处理可能的二进制数据）
            String errorMessage = "响应码: " + responseCode;
            try (InputStream errorStream = connection.getErrorStream()) {
                if (errorStream != null) {
                    // 先尝试读取为UTF-8文本
                    try (BufferedReader reader = new BufferedReader(
                            new InputStreamReader(errorStream, StandardCharsets.UTF_8))) {
                        StringBuilder errorResponse = new StringBuilder();
                        String line;
                        while ((line = reader.readLine()) != null) {
                            if (errorResponse.length() > 0) {
                                errorResponse.append("\n");
                            }
                            errorResponse.append(line);
                        }
                        if (errorResponse.length() > 0) {
                            errorMessage += ", 错误信息: " + errorResponse.toString();
                        }
                    } catch (Exception e) {
                        // 如果UTF-8解码失败，尝试读取前1024字节作为错误信息
                        log.warn("无法以UTF-8读取错误响应，尝试读取原始字节", e);
                        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
                            byte[] buffer = new byte[1024];
                            int bytesRead;
                            while ((bytesRead = errorStream.read(buffer)) != -1) {
                                baos.write(buffer, 0, bytesRead);
                            }
                            String rawError = new String(baos.toByteArray(), StandardCharsets.ISO_8859_1);
                            if (rawError.length() > 0) {
                                errorMessage += ", 原始错误信息: " + rawError.substring(0, Math.min(500, rawError.length()));
                            }
                        }
                    }
                }
            } catch (Exception e) {
                log.error("读取错误响应流失败", e);
                errorMessage += ", 无法读取错误详情";
            }
            log.error("音频模型API请求失败 - {}", errorMessage);
            throw new IOException("音频模型API请求失败，" + errorMessage);
        }
    }

    /**
     * 将chat/completions URL转换为audio/transcriptions URL
     *
     * @param originalUrl 原始URL（可能是/v1/chat/completions）
     * @return 转换后的音频转文字API URL（/v1/audio/transcriptions）
     */
    private String convertToAudioTranscriptionUrl(String originalUrl) {
        if (StringUtils.isBlank(originalUrl)) {
            return originalUrl;
        }

        log.info("开始URL转换 - 原始URL: {}", originalUrl);

        try {
            URL url = new URL(originalUrl);
            String path = url.getPath();
            log.info("URL路径解析 - path: {}", path);

            // 如果路径包含chat/completions，替换为audio/transcriptions
            if (path.contains("/chat/completions")) {
                String newPath = path.replace("/chat/completions", "/audio/transcriptions");
                StringBuilder newUrl = new StringBuilder();
                newUrl.append(url.getProtocol()).append("://").append(url.getHost());
                if (url.getPort() != -1) {
                    newUrl.append(":").append(url.getPort());
                }
                newUrl.append(newPath);
                if (url.getQuery() != null && !url.getQuery().isEmpty()) {
                    newUrl.append("?").append(url.getQuery());
                }
                String result = newUrl.toString();
                log.info("URL转换成功 - 原始: {}, 转换后: {}", originalUrl, result);
                return result;
            }

            // 如果路径已经是audio/transcriptions，直接返回
            if (path.contains("/audio/transcriptions")) {
                log.info("URL已经是audio/transcriptions端点，无需转换: {}", originalUrl);
                return originalUrl;
            }

            // 如果路径以/v1结尾或包含/v1/，尝试替换为/v1/audio/transcriptions
            if (path.endsWith("/v1") || path.contains("/v1/")) {
                StringBuilder baseUrl = new StringBuilder();
                baseUrl.append(url.getProtocol()).append("://").append(url.getHost());
                if (url.getPort() != -1) {
                    baseUrl.append(":").append(url.getPort());
                }
                // 提取基础路径（去掉/v1及之后的部分）
                int v1Index = path.indexOf("/v1");
                if (v1Index >= 0) {
                    String basePath = path.substring(0, v1Index);
                    if (!basePath.isEmpty()) {
                        baseUrl.append(basePath);
                    }
                }
                baseUrl.append("/v1/audio/transcriptions");
                if (url.getQuery() != null && !url.getQuery().isEmpty()) {
                    baseUrl.append("?").append(url.getQuery());
                }
                String result = baseUrl.toString();
                log.info("URL转换（拼接方式）成功 - 原始: {}, 转换后: {}", originalUrl, result);
                return result;
            }

            // 如果无法识别，直接返回原URL（可能已经是正确的）
            log.warn("无法识别URL格式，使用原始URL: {}", originalUrl);
            return originalUrl;
        } catch (Exception e) {
            log.error("URL转换失败，使用原始URL: {}", originalUrl, e);
            return originalUrl;
        }
    }

    /**
     * 处理MCP请求（Agent配置）
     *
     * @param request SSE请求参数
     * @param response HTTP响应对象
     * @param checkResp 权限验证结果
     * @param sign 签名
     * @return SSE流式响应
     */
    private SseEmitter handleMcpRequest(SseRequest request, HttpServletResponse response, CheckResp checkResp, String sign) {
        log.info("开始处理MCP请求 - mcpId: {}", request.getMcpId());

        // 设置SSE响应头
        response.setContentType(MediaType.TEXT_EVENT_STREAM_VALUE);
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Connection", "keep-alive");
        response.setHeader("X-Accel-Buffering", "no");

        SseEmitter emitter = new SseEmitter(86400000L); // 24小时超时

        // 异步处理MCP流式响应
        CompletableFuture.runAsync(() -> {
            try {
                processMcpStream(emitter, request, checkResp, sign);
            } catch (Exception e) {
                log.error("处理MCP流式响应失败", e);
                try {
                    emitter.send(SseEmitter.event()
                            .name("error")
                            .data("{\"error\":\"" + e.getMessage() + "\"}"));
                    emitter.complete();
                } catch (IOException ex) {
                    log.error("发送错误信息失败", ex);
                }
            }
        });

        return emitter;
    }

    /**
     * 处理MCP流式响应
     *
     * @param emitter SSE发射器
     * @param request SSE请求参数
     * @param checkResp 权限验证结果
     * @param sign 签名
     */
    private void processMcpStream(SseEmitter emitter, SseRequest request, CheckResp checkResp, String sign) throws IOException {
        try {
            log.info("开始处理MCP流式响应 - mcpId: {}", request.getMcpId());

            // 1. 获取RAG认证Token
            String token = getRagAuthToken();
            if (StringUtils.isBlank(token)) {
                log.error("获取RAG认证Token失败，无法调用MCP接口");
                emitter.send(SseEmitter.event()
                        .name("error")
                        .data("{\"error\":\"获取RAG认证Token失败\"}"));
                emitter.complete();
                return;
            }

            // 2. 获取处理后的prompt（如果存在）
            String processedPrompt = null;
            if (checkResp != null && checkResp.getSysPrompt() != null && StringUtils.isNotBlank(checkResp.getSysPrompt().getPrompt())) {
                processedPrompt = checkResp.getSysPrompt().getPrompt();
                log.info("MCP处理：检测到处理后的prompt，将用于替换所有message的content - prompt长度: {}", processedPrompt.length());
            }

            // 3. 转换messages为content格式（如果存在处理后的prompt，则使用prompt替换content）
            List<Map<String, Object>> messages = request.getMessages();
            List<Map<String, Object>> content = convertMessagesToContent(messages, processedPrompt);

            // 4. 构建MCP请求数据
            Map<String, Object> requestData = new HashMap<>();
            requestData.put("messages", content);
            requestData.put("model", "qwen3-32B");
            requestData.put("temperature", 0.5);
            requestData.put("top_p", 1.0);
            requestData.put("presence_penalty", 0);
            requestData.put("frequency_penalty", 0);
            requestData.put("kid", "");
            requestData.put("chat_type", 0);
            requestData.put("appId", "");
            requestData.put("agentId", request.getMcpId());
            requestData.put("stream", true);

            String jsonData = JSONUtil.toJsonStr(requestData);
            log.info("MCP请求数据: {}", jsonData.length() > 500 ? jsonData.substring(0, 500) + "..." : jsonData);

            // 4. 构建MCP请求URL
            String url = emoonMatchingUrl + "/chat/emoonSend";
            log.info("MCP接口URL: {}", url);

            // 5. 发送HTTP POST请求
            URL requestUrl = new URL(url);
            HttpURLConnection connection = (HttpURLConnection) requestUrl.openConnection();
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            connection.setConnectTimeout(60000);
            connection.setReadTimeout(86400000);
            connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
            connection.setRequestProperty("Accept", "text/event-stream");
            connection.setRequestProperty("Authorization", "Bearer " + token);
            connection.setRequestProperty("Cache-Control", "no-cache");
            connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");

            // 发送请求数据
            try (OutputStream os = connection.getOutputStream()) {
                os.write(jsonData.getBytes(StandardCharsets.UTF_8));
                os.flush();
            }

            // 检查HTTP响应码
            int responseCode = connection.getResponseCode();
            log.info("MCP接口响应码: {}", responseCode);

            if (responseCode != HttpURLConnection.HTTP_OK) {
                // 读取错误响应
                try (BufferedReader errorReader = new BufferedReader(
                        new InputStreamReader(connection.getErrorStream(), StandardCharsets.UTF_8))) {
                    StringBuilder errorResponse = new StringBuilder();
                    String errorLine;
                    while ((errorLine = errorReader.readLine()) != null) {
                        errorResponse.append(errorLine);
                    }
                    log.error("MCP接口调用失败，响应码: {}, 错误信息: {}", responseCode, errorResponse.toString());
                    emitter.send(SseEmitter.event()
                            .name("error")
                            .data("{\"error\":\"MCP接口调用失败，响应码: " + responseCode + "\"}"));
                }
                emitter.complete();
                return;
            }

            // 6. 生成chatId和created时间戳
            String chatId = "chat" + System.currentTimeMillis() + Long.toHexString(new Random().nextLong());
            long created = System.currentTimeMillis();

            // 7. 声明变量存储模型返回的完整内容
            StringBuilder reply = new StringBuilder("SSE: ");

            // 8. 读取流式响应并转换为SSE格式
            log.info("========== 开始读取MCP流式响应 ==========");
            StringBuilder rawResponse = new StringBuilder(); // 存储原始响应内容
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
                String line;
                String lastContent = "";
                int lineCount = 0;
                boolean inDataBlock = false; // 标记是否在处理data块
                StringBuilder currentDataBlock = new StringBuilder(); // 当前data块的内容

                while ((line = reader.readLine()) != null) {
                    lineCount++;
                    // 累积原始响应内容
                    rawResponse.append(line).append("\n");

                    // 打印原始响应行（前100行详细打印，后续每50行打印一次）
                    if (lineCount <= 100 || lineCount % 50 == 0) {
                        log.info("MCP原始响应行[{}]: [长度:{}] {}", lineCount, line.length(),
                                line.length() > 300 ? line.substring(0, 300) + "..." : line);
                    } else {
                        log.debug("MCP原始响应行[{}]: [长度:{}] {}", lineCount, line.length(),
                                line.length() > 300 ? line.substring(0, 300) + "..." : line);
                    }

                    // 处理以 "event:chat_completion" 开头的行
                    if (line.startsWith("event:chat_completion")) {
                        // 如果之前有未处理的data块，先处理它
                        if (inDataBlock && currentDataBlock.length() > 0) {
                            try {
                                String extractedContent = processDataBlock(emitter, chatId, created, currentDataBlock.toString(), lastContent, reply);
                                if (StringUtils.isNotBlank(extractedContent)) {
                                    lastContent = extractedContent;
                                }
                            } catch (IllegalStateException e) {
                                log.warn("emitter已关闭，停止处理MCP流式响应");
                                break; // 跳出循环
                            }
                            currentDataBlock.setLength(0); // 清空
                        }
                        // 开始新的data块
                        inDataBlock = true;
                        currentDataBlock.setLength(0);
                        log.debug("MCP检测到event:chat_completion，开始新的data块");
                    }
                    // 处理以 "data:" 开头的行
                    else if (line.startsWith("data:")) {
                        if (inDataBlock) {
                            // 提取data:后面的内容
                            String dataContent = line.substring(5); // 去掉"data:"前缀
                            currentDataBlock.append(dataContent);
                            log.debug("MCP追加data内容[{}]: {}", lineCount, dataContent.length() > 50 ? dataContent.substring(0, 50) + "..." : dataContent);
                        } else {
                            log.debug("MCP检测到data行但不在data块中: {}", line);
                        }
                    }
                    // 空行可能表示data块结束
                    else if (line.trim().isEmpty() && inDataBlock && currentDataBlock.length() > 0) {
                        // 处理完整的data块
                        try {
                            String extractedContent = processDataBlock(emitter, chatId, created, currentDataBlock.toString(), lastContent, reply);
                            if (StringUtils.isNotBlank(extractedContent)) {
                                lastContent = extractedContent;
                            }
                        } catch (IllegalStateException e) {
                            log.warn("emitter已关闭，停止处理MCP流式响应");
                            break; // 跳出循环
                        }
                        currentDataBlock.setLength(0);
                        inDataBlock = false;
                        log.debug("MCP检测到空行，处理data块完成");
                    }
                }

                // 处理最后一个data块（如果存在）
                if (inDataBlock && currentDataBlock.length() > 0) {
                    try {
                        String extractedContent = processDataBlock(emitter, chatId, created, currentDataBlock.toString(), lastContent, reply);
                        log.debug("MCP处理最后一个data块，提取内容长度: {}", extractedContent != null ? extractedContent.length() : 0);
                    } catch (IllegalStateException e) {
                        log.warn("emitter已关闭，无法处理最后一个data块");
                    }
                }
                log.info("MCP流式响应读取完成，共处理 {} 行", lineCount);

                // 打印完整的原始响应内容
                String rawResponseStr = rawResponse.toString();
                log.info("========== MCP完整原始响应内容 ==========");
                log.info("MCP原始响应总长度: {} 字符", rawResponseStr.length());
                log.info("MCP原始响应内容（前5000字符）: {}",
                        rawResponseStr.length() > 5000 ? rawResponseStr.substring(0, 5000) + "..." : rawResponseStr);
                log.info("==========================================");
            }
            log.info("=========================================");

            // 9. 发送结束标记
            try {
                emitter.send(SseEmitter.event()
                        .name("end")
                        .data("{\"status\":\"done\"}"));
            } catch (IllegalStateException e) {
                log.warn("发送结束标记失败，emitter已关闭", e);
            }

            // 10. 完成emitter
            try {
                emitter.complete();
            } catch (IllegalStateException e) {
                log.warn("emitter已完成，忽略重复完成操作", e);
            }

            // 11. 返回完整的回复内容（去掉前缀"SSE: "）
            String finalReply = reply.toString();
            if (finalReply.startsWith("SSE: ")) {
                finalReply = finalReply.substring(5);
            }

            // 12. 打印完整的MCP返回内容
            log.info("========== MCP完整返回内容 ==========");
            log.info("MCP返回内容长度: {}", finalReply.length());
            log.info("MCP返回内容: {}", finalReply.length() > 2000 ? finalReply.substring(0, 2000) + "..." : finalReply);
            log.info("====================================");

            // 13. 保存请求记录到数据库
            saveAskInfo(request, sign, finalReply);

            log.info("MCP流式响应处理完成，mcpId: {}, 回复长度: {}", request.getMcpId(), finalReply.length());

        } catch (Exception e) {
            log.error("处理MCP流式响应失败，mcpId: {}", request.getMcpId(), e);
            try {
                emitter.send(SseEmitter.event()
                        .name("error")
                        .data("{\"error\":\"" + e.getMessage() + "\"}"));
            } catch (IllegalStateException ex) {
                log.warn("发送错误信息失败，emitter已关闭", ex);
            } finally {
                try {
                    emitter.complete();
                } catch (IllegalStateException ex) {
                    log.warn("完成emitter失败，可能已经关闭", ex);
                }
            }
        }
    }

    /**
     * 处理MCP data块，解析JSON并提取data字段内容
     *
     * @param emitter SSE发射器
     * @param chatId 聊天ID
     * @param created 创建时间戳
     * @param dataBlockContent data块内容（拼接后的JSON字符串）
     * @param lastContent 上次的内容（用于去重）
     * @param reply 回复内容累积器
     * @return 提取的内容
     * @throws IOException 如果发送SSE事件失败
     */
    private String processDataBlock(SseEmitter emitter, String chatId, long created,
                                   String dataBlockContent, String lastContent, StringBuilder reply) throws IOException {
        try {
            if (StringUtils.isBlank(dataBlockContent)) {
                log.debug("MCP data块内容为空，跳过处理");
                return "";
            }

            log.info("MCP处理data块，内容长度: {}, 内容预览: {}",
                    dataBlockContent.length(),
                    dataBlockContent.length() > 200 ? dataBlockContent.substring(0, 200) + "..." : dataBlockContent);

            // 尝试解析JSON
            JSONObject jsonData;
            try {
                jsonData = JSONUtil.parseObj(dataBlockContent);
            } catch (Exception e) {
                // 如果解析失败，可能是JSON不完整，尝试提取data字段
                log.warn("MCP data块JSON解析失败，尝试直接提取: {}", e.getMessage());
                // 如果解析失败，可能data块内容就是纯文本，直接使用
                String content = dataBlockContent.trim();
                if (StringUtils.isNotBlank(content) && !content.equals(lastContent)) {
                    sendChunkContent(emitter, chatId, created, content, reply);
                    return content;
                }
                return "";
            }

            // 提取data字段
            Object dataObj = jsonData.get("data");
            if (dataObj == null) {
                log.debug("MCP JSON中未找到data字段");
                return "";
            }

            String content;
            if (dataObj instanceof String) {
                content = (String) dataObj;
            } else {
                // 如果data不是字符串，转换为字符串
                content = JSONUtil.toJsonStr(dataObj);
            }

            // 去除引号（如果content是JSON字符串格式）
            if (content.startsWith("\"") && content.endsWith("\"")) {
                content = content.substring(1, content.length() - 1);
            }

            log.info("MCP从data字段提取的内容: {}", content.length() > 100 ? content.substring(0, 100) + "..." : content);

            // 仅当内容变化时才输出
            if (StringUtils.isNotBlank(content) && !content.equals(lastContent)) {
                sendChunkContent(emitter, chatId, created, content, reply);
                return content;
            }

            return "";
        } catch (IOException e) {
            // 重新抛出IOException，让外层处理
            throw e;
        } catch (Exception e) {
            log.error("处理MCP data块失败", e);
            return "";
        }
    }

    /**
     * 发送chunk内容到SSE
     *
     * @param emitter SSE发射器
     * @param chatId 聊天ID
     * @param created 创建时间戳
     * @param content 内容
     * @param reply 回复内容累积器
     * @throws IOException 如果发送SSE事件失败
     */
    private void sendChunkContent(SseEmitter emitter, String chatId, long created,
                                 String content, StringBuilder reply) throws IOException {
        // 构建SSE响应格式
        Map<String, Object> jsonDataMap = new HashMap<>();
        jsonDataMap.put("id", chatId);
        jsonDataMap.put("model", "emoon-E1-13B");
        jsonDataMap.put("created", created);
        jsonDataMap.put("object", "chat.completion.chunk");

        Map<String, Object> choice = new HashMap<>();
        Map<String, Object> delta = new HashMap<>();
        delta.put("content", content);
        choice.put("index", 0);
        choice.put("delta", delta);
        choice.put("finish_reason", null);

        List<Map<String, Object>> choices = new ArrayList<>();
        choices.add(choice);
        jsonDataMap.put("choices", choices);
        jsonDataMap.put("usage", null);

        String jsonStr = JSONUtil.toJsonStr(jsonDataMap);
        log.info("MCP构建的SSE响应数据: {}", jsonStr.length() > 500 ? jsonStr.substring(0, 500) + "..." : jsonStr);

        // 发送SSE事件
        try {
            emitter.send(SseEmitter.event()
                    .data(jsonStr));
        } catch (IllegalStateException e) {
            log.warn("emitter已关闭，停止读取流式数据");
            throw e; // 抛出异常以便外层处理
        }

        // 累积回复内容
        if (StringUtils.isNotBlank(content)) {
            reply.append(content);
        }
    }

    /**
     * 处理prompt中的占位符替换
     *
     * @param prompt 原始prompt字符串，包含${字段名}格式的占位符
     * @param payload JSON字符串格式的payload数据
     * @return 替换后的prompt字符串
     */
    private String processPromptPlaceholders(String prompt, String payload) {
        if (StringUtils.isBlank(prompt)) {
            return prompt;
        }

        if (StringUtils.isBlank(payload)) {
            log.debug("payload为空，清理prompt中所有占位符");
            return cleanRemainingPlaceholders(prompt);
        }

        try {
            // 1. 反序列化payload
            JSONObject payloadJson = JSONUtil.parseObj(payload);

            // 2. 递归遍历payload，收集所有最底层的字段名和值
            Map<String, String> fieldValueMap = new HashMap<>();
            collectFieldValues(payloadJson, "", fieldValueMap);

            log.info("payload参数: {}", payloadJson);
            log.info("提取的字段: {}", fieldValueMap);
            log.info("prompt参数: {}", prompt);
            // 3. 替换prompt中的占位符
            String result = prompt;
            for (Map.Entry<String, String> entry : fieldValueMap.entrySet()) {
                String fieldName = entry.getKey();
                String fieldValue = entry.getValue() != null ? entry.getValue() : "";
                String placeholder = "${" + fieldName + "}";
                if (result.contains(placeholder)) {
                    result = result.replace(placeholder, fieldValue);
                    log.info("替换占位符: {} -> {}", placeholder, fieldValue);
                }
            }
            log.info("替换占位符后: {}", result);
            // 4. 清理剩余的占位符（替换成空字符串）
            result = cleanRemainingPlaceholders(result);

            return result;
        } catch (Exception e) {
            log.error("处理prompt占位符失败，返回清理后的prompt", e);
            // 如果处理失败，至少清理剩余的占位符
            return cleanRemainingPlaceholders(prompt);
        }
    }

    /**
     * 递归收集JSON对象中所有最底层的字段名和值
     *
     * @param obj JSON对象或数组
     * @param prefix 字段名前缀（用于嵌套字段）
     * @param fieldValueMap 存储字段名和值的Map
     */
    private void collectFieldValues(Object obj, String prefix, Map<String, String> fieldValueMap) {
        if (obj == null) {
            return;
        }

        if (obj instanceof JSONObject) {
            JSONObject jsonObject = (JSONObject) obj;
            for (String key : jsonObject.keySet()) {
                Object value = jsonObject.get(key);
                String fieldName = StringUtils.isBlank(prefix) ? key : prefix + "." + key;

                if (value == null) {
                    fieldValueMap.put(key, null);
                } else if (value instanceof JSONObject || value instanceof JSONArray) {
                    // 递归处理嵌套对象或数组
                    collectFieldValues(value, fieldName, fieldValueMap);
                } else if (value instanceof String || value instanceof Number || value instanceof Boolean) {
                    // 最底层的字段，存储值
                    fieldValueMap.put(key, String.valueOf(value));
                    // 同时存储完整路径的字段名（如果存在嵌套）
                    if (!fieldName.equals(key)) {
                        fieldValueMap.put(fieldName, String.valueOf(value));
                    }
                } else {
                    // 其他类型，转换为字符串
                    fieldValueMap.put(key, String.valueOf(value));
                }
            }
        } else if (obj instanceof JSONArray) {
            JSONArray jsonArray = (JSONArray) obj;
            for (int i = 0; i < jsonArray.size(); i++) {
                Object item = jsonArray.get(i);
                collectFieldValues(item, prefix, fieldValueMap);
            }
        }
    }

    /**
     * 清理prompt中剩余的占位符（替换成空字符串）
     *
     * @param prompt 待清理的prompt字符串
     * @return 清理后的字符串
     */
    private String cleanRemainingPlaceholders(String prompt) {
        if (StringUtils.isBlank(prompt)) {
            return prompt;
        }

        // 使用正则表达式匹配所有${...}格式的占位符
        String result = prompt.replaceAll("\\$\\{[^}]+\\}", "");
        return result;
    }


}
