package top.tqx.demo_1.service.impl;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import top.tqx.demo_1.config.AiApiConfig;
import top.tqx.demo_1.dto.RagRequest;
import top.tqx.demo_1.dto.ReferenceResponse;
import top.tqx.demo_1.entity.ChatMessage;
import top.tqx.demo_1.entity.ChatReference;
import top.tqx.demo_1.entity.ChatSession;
import top.tqx.demo_1.entity.Image;
import top.tqx.demo_1.service.AiChatService;
import top.tqx.demo_1.service.ChatService;
import top.tqx.demo_1.service.ImageService;
import top.tqx.demo_1.service.SseEmitterManager;
import top.tqx.demo_1.service.DepartmentService;
import top.tqx.demo_1.service.KnowledgeBasePathService;

import org.springframework.beans.factory.annotation.Value;

import java.math.BigDecimal;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

@Slf4j
@Service
@SuppressWarnings("unchecked")
public class AiChatServiceImpl implements AiChatService {

    @Autowired
    private AiApiConfig aiApiConfig;

    @Autowired
    private ChatService chatService;

    @Autowired
    private SseEmitterManager sseEmitterManager;

    @Autowired
    private ImageService imageService;

    // Nginx 图片访问前缀（从配置文件读取）
    @Value("${file.upload.url-prefix:/files/}")
    private String imageUrlPrefix;

    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public SseEmitter streamFromRagService(RagRequest request, Long userId, Long deptId) throws Exception {
        SseEmitter emitter = new SseEmitter(300000L);

        String sessionId = request.getSessionId();

        if (sessionId != null) {
            sseEmitterManager.register(sessionId, emitter);
            emitter.onCompletion(() -> sseEmitterManager.unregister(sessionId));
            emitter.onTimeout(() -> sseEmitterManager.unregister(sessionId));
            emitter.onError(e -> sseEmitterManager.unregister(sessionId));
        }

        final Long[] chatSessionId = {null};
        final ChatSession[] chatSession = {null};
        final ChatMessage[] savedAssistantMessage = {null};
        final List<ChatReference>[] referenceList = new List[1];

        CompletableFuture.runAsync(() -> {
            final boolean[] emitterActive = {true};
            try {
                String ragStreamUrl = aiApiConfig.getFullUrl(aiApiConfig.getChat().getRagStream());
                log.info("开始调用RAG服务: url={}, userId={}, message={}, collections={}, sessionId={}",
                        ragStreamUrl, userId, request.getMessage(), request.getCollections(), sessionId);

                chatSession[0] = chatService.getOrCreateSession(userId, sessionId);
                chatSessionId[0] = chatSession[0].getId();

                String kbPathsJson = objectMapper.writeValueAsString(request.getCollections());

                ObjectNode requestBody = objectMapper.createObjectNode();
                requestBody.put("message", request.getMessage());
                requestBody.set("collections", objectMapper.valueToTree(request.getCollections()));

                List<RagRequest.HistoryItem> history = request.getHistory();
                if (history == null) {
                    history = new ArrayList<>();
                }
                requestBody.set("chat_history", objectMapper.valueToTree(history));

                if (sessionId != null) {
                    requestBody.put("session_id", sessionId);
                }
                if (request.getId() != null) {
                    requestBody.put("id", request.getId());
                }

                final StringBuilder fullAnswer = new StringBuilder();
                final String[] finalFullAnswer = {""};
                final List<ChatReference>[] finalReferences = new List[1];
        finalReferences[0] = new ArrayList<>();
        referenceList[0] = new ArrayList<>();
        final List<ChatReference>[] citationList = new List[1];
        citationList[0] = new ArrayList<>();
        final List<String>[] imageIds = new List[1];
        imageIds[0] = new ArrayList<>();
        final List<java.util.Map<String, Object>>[] imageMetadataList = new List[1];
        imageMetadataList[0] = new java.util.ArrayList<>();
        final java.util.Set<String>[] processedChunkIds = new java.util.Set[1];
        processedChunkIds[0] = new java.util.HashSet<>();
        long startTime = System.currentTimeMillis();

                log.info("开始流式调用RAG服务...");

                try {
                    java.net.URL url = new java.net.URL(ragStreamUrl);
                    java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();
                    connection.setRequestMethod("POST");
                    connection.setDoOutput(true);
                    connection.setDoInput(true);
                    connection.setUseCaches(false);
                    connection.setRequestProperty("Content-Type", "application/json");
                    connection.setConnectTimeout(30000);
                    connection.setReadTimeout(300000);

                    connection.connect();

                    try (java.io.OutputStream os = connection.getOutputStream()) {
                        byte[] input = requestBody.toString().getBytes(java.nio.charset.StandardCharsets.UTF_8);
                        os.write(input, 0, input.length);
                    }

                    int responseCode = connection.getResponseCode();
                    log.info("RAG服务响应状态码: {}", responseCode);

                    if (responseCode == 200) {
                        try (java.io.BufferedReader reader = new java.io.BufferedReader(
                                new java.io.InputStreamReader(connection.getInputStream(), java.nio.charset.StandardCharsets.UTF_8))) {
                            String line;
                            while ((line = reader.readLine()) != null) {
                                log.debug("📥 收到RAG原始数据: {}", line);

                                if (line.trim().isEmpty()) {
                                    continue;
                                }

                                if (line.startsWith("data: ")) {
                                    String jsonData = line.substring(6).trim();

                                    if (jsonData.equals("[DONE]")) {
                                        log.info("RAG服务流式响应结束");
                                        break;
                                    }

                                    try {
                                        JsonNode jsonNode = objectMapper.readTree(jsonData);
                                        String type = jsonNode.has("type") ? jsonNode.get("type").asText() : "";

                                        SseEmitter.SseEventBuilder event = SseEmitter.event()
                                                .data(jsonNode);

                                        switch (type) {
                                            case "start":
                                                log.info("开始处理: {}", jsonNode.has("message") ? jsonNode.get("message").asText() : "");
                                                break;

                                            case "connected":
                                                log.info("RAG连接成功: {}", jsonNode.has("message") ? jsonNode.get("message").asText() : "");
                                                break;

                                            case "thinking":
                                                log.debug("RAG思考中: step={}, content={}",
                                                        jsonNode.has("step") ? jsonNode.get("step").asText() : "",
                                                        jsonNode.has("content") ? jsonNode.get("content").asText() : "");
                                                break;

                                            case "searching":
                                                log.debug("RAG检索知识库: step={}, collection={}",
                                                        jsonNode.has("step") ? jsonNode.get("step").asText() : "",
                                                        jsonNode.has("collection") ? jsonNode.get("collection").asText() : "");
                                                break;

                                            case "sources":
                                                log.info("收到检索来源信息");
                                                if (jsonNode.has("sources")) {
                                                    JsonNode sourcesNode = jsonNode.get("sources");
                                                    if (sourcesNode.isArray()) {
                                                        for (JsonNode sourceItem : sourcesNode) {
                                                            ChatReference ref = parseSourceToReference(sourceItem);
                                                            String chunkId = ref.getChunkId();
                                                            if (chunkId == null || chunkId.isEmpty()) {
                                                                chunkId = ref.getDocName() + "_" + ref.getPage() + "_" + ref.getSectionChunkId();
                                                            }
                                                            if (!processedChunkIds[0].contains(chunkId)) {
                                                                processedChunkIds[0].add(chunkId);
                                                                referenceList[0].add(ref);
                                                            }
                                                        }
                                                    }
                                                }
                                                break;

                                            case "result":
                                            case "finish":
                                                log.info("RAG返回最终结果: type={}", type);
                                                String answerFromFinish = "";
                                                if (jsonNode.has("answer")) {
                                                    answerFromFinish = jsonNode.get("answer").asText();
                                                    finalFullAnswer[0] = answerFromFinish;
                                                    log.info("提取到finish事件的answer: length={}", answerFromFinish.length());
                                                }
                                                if (jsonNode.has("sources")) {
                                                    JsonNode sourcesNode = jsonNode.get("sources");
                                                    if (sourcesNode.isArray()) {
                                                        for (JsonNode sourceItem : sourcesNode) {
                                                            ChatReference ref = parseSourceToReference(sourceItem);
                                                            String chunkId = ref.getChunkId();
                                                            if (chunkId == null || chunkId.isEmpty()) {
                                                                chunkId = ref.getDocName() + "_" + ref.getPage() + "_" + ref.getSectionChunkId();
                                                            }
                                                            if (!processedChunkIds[0].contains(chunkId)) {
                                                                processedChunkIds[0].add(chunkId);
                                                                referenceList[0].add(ref);
                                                            }
                                                        }
                                                        log.info("从finish事件提取sources: count={}", sourcesNode.size());
                                                    }
                                                }
                                                if (jsonNode.has("citations")) {
                                                    JsonNode citationsNode = jsonNode.get("citations");
                                                    if (citationsNode.isArray()) {
                                                        for (JsonNode citationItem : citationsNode) {
                                                            ChatReference ref = parseCitationToReference(citationItem);
                                                            String chunkId = ref.getChunkId();
                                                            if (chunkId == null || chunkId.isEmpty()) {
                                                                chunkId = ref.getDocName() + "_" + ref.getPage() + "_" + ref.getSectionChunkId();
                                                            }
                                                            if (!processedChunkIds[0].contains(chunkId)) {
                                                                processedChunkIds[0].add(chunkId);
                                                                citationList[0].add(ref);
                                                                referenceList[0].add(ref);
                                                            }
                                                        }
                                                        log.info("从finish事件提取citations: count={}", citationsNode.size());
                                                    }
                                                }
                                                if (jsonNode.has("images")) {
                                                    JsonNode imagesNode = jsonNode.get("images");
                                                    log.info("[{}] [图片提取] 开始 - 检测到images数组, size={}", java.time.LocalDateTime.now(), imagesNode.isArray() ? imagesNode.size() : "not array");
                                                    if (imagesNode.isArray()) {
                                                        int processedCount = 0;
                                                        int skippedCount = 0;
                                                        for (JsonNode imageNode : imagesNode) {
                                                            String imageId = null;
                                                            if (imageNode.has("id")) {
                                                                imageId = imageNode.get("id").asText();
                                                            } else if (imageNode.has("image_id")) {
                                                                imageId = imageNode.get("image_id").asText();
                                                            }
                                                            
                                                            if (imageId == null) {
                                                                log.warn("[{}] [图片提取] 跳过 - 图片ID为空", java.time.LocalDateTime.now());
                                                                skippedCount++;
                                                                continue;
                                                            }
                                                            
                                                            if (imageIds[0].contains(imageId)) {
                                                                log.debug("[{}] [图片提取] 跳过 - 图片ID已存在, imageId={}", java.time.LocalDateTime.now(), imageId);
                                                                skippedCount++;
                                                                continue;
                                                            }
                                                            
                                                            imageIds[0].add(imageId);
                                                            log.info("[{}] [图片提取] 处理 - 提取图片ID: {}", java.time.LocalDateTime.now(), imageId);
                                                            
                                                            java.util.Map<String, Object> imageMetadata = new java.util.HashMap<>();
                                                            imageMetadata.put("id", imageId);
                                                            
                                                            if (imageNode.has("source")) {
                                                                imageMetadata.put("source", imageNode.get("source").asText());
                                                                log.debug("[{}] [图片提取] 设置source={}", java.time.LocalDateTime.now(), imageMetadata.get("source"));
                                                            } else {
                                                                log.warn("[{}] [图片提取] 警告 - 缺少source字段, imageId={}", java.time.LocalDateTime.now(), imageId);
                                                            }
                                                            
                                                            if (imageNode.has("page")) {
                                                                imageMetadata.put("page", imageNode.get("page").asInt());
                                                                log.debug("[{}] [图片提取] 设置page={}", java.time.LocalDateTime.now(), imageMetadata.get("page"));
                                                            } else {
                                                                log.warn("[{}] [图片提取] 警告 - 缺少page字段, imageId={}", java.time.LocalDateTime.now(), imageId);
                                                            }
                                                            
                                                            if (imageNode.has("caption")) {
                                                                imageMetadata.put("description", imageNode.get("caption").asText());
                                                            } else if (imageNode.has("description")) {
                                                                imageMetadata.put("description", imageNode.get("description").asText());
                                                            }
                                                            if (imageMetadata.containsKey("description")) {
                                                                String desc = (String) imageMetadata.get("description");
                                                                log.debug("[{}] [图片提取] 设置description(前50字符)={}", java.time.LocalDateTime.now(), 
                                                                        desc.length() > 50 ? desc.substring(0, 50) + "..." : desc);
                                                            }
                                                            
                                                            if (imageNode.has("full_description")) {
                                                                imageMetadata.put("full_description", imageNode.get("full_description").asText());
                                                                log.debug("[{}] [图片提取] 设置full_description长度={}", java.time.LocalDateTime.now(), 
                                                                        imageMetadata.get("full_description").toString().length());
                                                            }
                                                            
                                                            if (imageNode.has("type")) {
                                                                imageMetadata.put("type", imageNode.get("type").asText());
                                                                log.debug("[{}] [图片提取] 设置type={}", java.time.LocalDateTime.now(), imageMetadata.get("type"));
                                                            } else {
                                                                log.warn("[{}] [图片提取] 警告 - 缺少type字段, imageId={}", java.time.LocalDateTime.now(), imageId);
                                                            }
                                                            
                                                            if (imageNode.has("score")) {
                                                                imageMetadata.put("score", imageNode.get("score").asDouble());
                                                                log.debug("[{}] [图片提取] 设置score={}", java.time.LocalDateTime.now(), imageMetadata.get("score"));
                                                            }
                                                            
                                                            if (imageNode.has("url")) {
                                                                imageMetadata.put("url", imageNode.get("url").asText());
                                                            } else {
                                                                imageMetadata.put("url", "/api/image/" + imageId + "/data");
                                                            }
                                                            log.debug("[{}] [图片提取] 设置url={}", java.time.LocalDateTime.now(), imageMetadata.get("url"));
                                                            
                                                            imageMetadataList[0].add(imageMetadata);
                                                            processedCount++;
                                                            log.info("[{}] [图片提取] 完成 - 图片元数据已添加, imageId={}, metadataKeys={}", 
                                                                    java.time.LocalDateTime.now(), imageId, imageMetadata.keySet());
                                                        }
                                                        log.info("[{}] [图片提取] 结束 - 处理完成, 总数={}, 已处理={}, 跳过={}", 
                                                                java.time.LocalDateTime.now(), imagesNode.size(), processedCount, skippedCount);
                                                    }
                                                }
                                                break;  // 发送finish事件给前端

                                            case "chunk":
                                                if (jsonNode.has("content")) {
                                                    String chunkContent = jsonNode.get("content").asText();
                                                    fullAnswer.append(chunkContent);
                                                    log.debug("📤 收到chunk, 累计长度: {}", fullAnswer.length());
                                                }
                                                break;

                                            case "image":
                                                log.info("收到图片信息");
                                                if (jsonNode.has("image_id")) {
                                                    String imageId = jsonNode.get("image_id").asText();
                                                    imageIds[0].add(imageId);
                                                    log.info("收集到图片ID: {}", imageId);
                                                }
                                                break;

                                            case "images":
                                                log.info("收到图片列表");
                                                if (jsonNode.has("images") && jsonNode.get("images").isArray()) {
                                                    for (JsonNode imageNode : jsonNode.get("images")) {
                                                        if (imageNode.has("image_id")) {
                                                            String imageId = imageNode.get("image_id").asText();
                                                            imageIds[0].add(imageId);
                                                            log.info("收集到图片ID: {}", imageId);
                                                        }
                                                    }
                                                }
                                                break;

                                            case "error":
                                                log.error("RAG返回错误: {}", jsonNode);
                                                break;

                                            default:
                                                log.debug("RAG未知事件类型: {}", type);
                                                break;
                                        }

                                        emitter.send(event);
                                        log.debug("📤 SSE事件已发送: type={}", type);

                                    } catch (Exception e) {
                                        log.warn("解析RAG SSE数据失败: {}, data={}", e.getMessage(), jsonData);
                                        if (e.getMessage() != null && (e.getMessage().contains("Broken pipe") || e.getMessage().contains("has already completed") || e.getMessage().contains("failed to flush"))) {
                                            log.error("SSE连接已断开，停止处理: {}", e.getMessage());
                                            emitterActive[0] = false;
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                    } else {
                        log.error("RAG服务返回错误状态码: {}", responseCode);
                    }

                    connection.disconnect();

                } catch (Exception e) {
                    log.error("调用RAG服务失败: {}", e.getMessage(), e);
                    
                    if (emitterActive[0]) {
                        try {
                            ObjectNode errorNode = objectMapper.createObjectNode();
                            errorNode.put("type", "error");
                            
                            if (e instanceof java.net.SocketTimeoutException) {
                                errorNode.put("message", "RAG服务响应超时，请稍后重试或简化问题");
                            } else if (e.getMessage() != null && e.getMessage().contains("has already completed")) {
                                log.warn("SSE连接已提前关闭，跳过发送错误事件");
                                return;
                            } else {
                                errorNode.put("message", "AI服务暂时不可用，请稍后重试");
                            }
                            
                            if (e.getMessage() != null && !e.getMessage().contains("has already completed")) {
                                errorNode.put("detail", e.getMessage());
                            }

                            SseEmitter.SseEventBuilder errorEvent = SseEmitter.event()
                                    .name("error")
                                    .data(errorNode);
                            emitter.send(errorEvent);
                        } catch (Exception ex) {
                            log.error("发送错误事件失败", ex);
                        }
                        try {
                            emitter.complete();
                        } catch (Exception ex) {
                            log.warn("emitter.complete()失败: {}", ex.getMessage());
                        }
                    }
                    return;
                }

                String finalAnswer = finalFullAnswer[0];
                log.info("检查回答内容: finalFullAnswer.length={}, fullAnswer.length={}", 
                        finalFullAnswer[0].length(), fullAnswer.length());
                
                if (finalAnswer.isEmpty()) {
                    log.info("finalFullAnswer为空，使用fullAnswer");
                    finalAnswer = fullAnswer.toString();
                }
                long durationMs = System.currentTimeMillis() - startTime;

                log.info("准备保存消息: finalAnswer.length={}, references.size={}, images.size={}",
                        finalAnswer.length(), referenceList[0].size(), imageIds[0].size());

                if (!finalAnswer.isEmpty()) {
                    try {
                        String saveOperationId = java.util.UUID.randomUUID().toString().substring(0, 8);
                        log.info("[{}] [{}] ========== 图片保存流程开始 ==========", java.time.LocalDateTime.now(), saveOperationId);
                        
                        if (!referenceList[0].isEmpty()) {
                            log.info("[{}] [{}] [来源图片处理] 开始 - 检查来源中的图片信息, referenceCount={}", 
                                    java.time.LocalDateTime.now(), saveOperationId, referenceList[0].size());
                            int chartImageCount = 0;
                            for (ChatReference ref : referenceList[0]) {
                                if ("chart".equals(ref.getChunkType()) || "image".equals(ref.getChunkType())) {
                                    chartImageCount++;
                                    if (ref.getChunkId() != null && !ref.getChunkId().isEmpty()) {
                                        try {
                                            log.info("[{}] [{}] [来源图片处理] 处理 - chunkId={}, docName={}, page={}, chunkType={}", 
                                                    java.time.LocalDateTime.now(), saveOperationId, 
                                                    ref.getChunkId(), ref.getDocName(), ref.getPage(), ref.getChunkType());
                                            
                                            java.util.Map<String, Object> imageMetadata = new java.util.HashMap<>();
                                            imageMetadata.put("id", ref.getChunkId());
                                            imageMetadata.put("source", ref.getDocName());
                                            imageMetadata.put("page", ref.getPage());
                                            imageMetadata.put("page_end", ref.getPageEnd());
                                            imageMetadata.put("page_range", ref.getPageRange());
                                            imageMetadata.put("section", ref.getSection());
                                            imageMetadata.put("chunk_type", ref.getChunkType());
                                            imageMetadata.put("score", ref.getScore() != null ? ref.getScore().doubleValue() : null);
                                            imageMetadata.put("url", "/api/image/" + ref.getChunkId() + "/data");

                                            log.debug("[{}] [{}] [来源图片处理] 调用downloadAndSaveImageWithMetadata, imageId={}", 
                                                    java.time.LocalDateTime.now(), saveOperationId, ref.getChunkId());
                                            Image savedImage = imageService.downloadAndSaveImageWithMetadata(imageMetadata);
                                            
                                            if (savedImage != null) {
                                                // 尝试获取Nginx格式的URL并更新到metadata
                                                String nginxUrl = imageService.getNginxUrlByImageId(ref.getChunkId());
                                                if (nginxUrl != null) {
                                                    imageMetadata.put("url", nginxUrl);
                                                    log.info("[{}] [{}] [来源图片处理] 更新 - 图片URL已更新为Nginx格式, imageId={}, url={}", 
                                                            java.time.LocalDateTime.now(), saveOperationId, ref.getChunkId(), nginxUrl);
                                                }
                                                imageIds[0].add(ref.getChunkId());
                                                imageMetadataList[0].add(imageMetadata);
                                                log.info("[{}] [{}] [来源图片处理] 成功 - 图片信息已保存到数据库, imageId={}, dbId={}", 
                                                        java.time.LocalDateTime.now(), saveOperationId, ref.getChunkId(), savedImage.getId());
                                            } else {
                                                log.warn("[{}] [{}] [来源图片处理] 失败 - 图片保存返回null, imageId={}", 
                                                        java.time.LocalDateTime.now(), saveOperationId, ref.getChunkId());
                                            }
                                        } catch (Exception e) {
                                            log.error("[{}] [{}] [来源图片处理] 异常 - 保存图片失败, imageId={}, error={}, stackTrace={}", 
                                                    java.time.LocalDateTime.now(), saveOperationId, ref.getChunkId(), 
                                                    e.getMessage(), org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace(e));
                                        }
                                    } else {
                                        log.warn("[{}] [{}] [来源图片处理] 跳过 - chunkId为空, chunkType={}, docName={}", 
                                                java.time.LocalDateTime.now(), saveOperationId, ref.getChunkType(), ref.getDocName());
                                    }
                                }
                            }
                            log.info("[{}] [{}] [来源图片处理] 结束 - 完成处理, 图表/图片数量={}", 
                                    java.time.LocalDateTime.now(), saveOperationId, chartImageCount);
                        }

                        if (!imageIds[0].isEmpty()) {
                            log.info("[{}] [{}] [图片处理] 开始 - 处理images列表中的图片, imageCount={}", 
                                    java.time.LocalDateTime.now(), saveOperationId, imageIds[0].size());
                            
                            // 1. 首先处理有元数据的图片（来自finish事件中的images数组）
                            for (java.util.Map<String, Object> imageMetadata : imageMetadataList[0]) {
                                try {
                                    String imageId = String.valueOf(imageMetadata.get("id"));
                                    log.info("[{}] [{}] [有元数据图片处理] 处理 - imageId={}", 
                                            java.time.LocalDateTime.now(), saveOperationId, imageId);
                                    log.debug("[{}] [{}] [有元数据图片处理] 调用downloadAndSaveImageWithMetadata, imageId={}", 
                                            java.time.LocalDateTime.now(), saveOperationId, imageId);
                                    
                                    Image savedImage = imageService.downloadAndSaveImageWithMetadata(imageMetadata);
                                    
                                    if (savedImage != null) {
                                        // 尝试获取Nginx格式的URL并更新到metadata
                                        String nginxUrl = imageService.getNginxUrlByImageId(imageId);
                                        if (nginxUrl != null) {
                                            imageMetadata.put("url", nginxUrl);
                                            log.info("[{}] [{}] [有元数据图片处理] 更新 - 图片URL已更新为Nginx格式, imageId={}, url={}", 
                                                    java.time.LocalDateTime.now(), saveOperationId, imageId, nginxUrl);
                                        }
                                        log.info("[{}] [{}] [有元数据图片处理] 成功 - 图片已保存到数据库, imageId={}, dbId={}, path={}", 
                                                java.time.LocalDateTime.now(), saveOperationId, imageId, savedImage.getId(), savedImage.getPath());
                                    } else {
                                        log.warn("[{}] [{}] [有元数据图片处理] 失败 - 图片保存返回null, imageId={}", 
                                                java.time.LocalDateTime.now(), saveOperationId, imageId);
                                    }
                                } catch (Exception e) {
                                    log.error("[{}] [{}] [有元数据图片处理] 异常 - 保存图片失败, error={}, stackTrace={}", 
                                            java.time.LocalDateTime.now(), saveOperationId, 
                                            e.getMessage(), org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace(e));
                                }
                            }
                            
                            // 2. 再处理没有元数据的图片（备用逻辑保留）
                            for (String imageId : imageIds[0]) {
                                if (imageMetadataList[0].stream().noneMatch(m -> imageId.equals(m.get("id")))) {
                                    try {
                                        log.info("[{}] [{}] [无元数据图片处理] 处理 - imageId={} (无元数据,需直接下载)", 
                                                java.time.LocalDateTime.now(), saveOperationId, imageId);
                                        log.debug("[{}] [{}] [无元数据图片处理] 调用downloadAndSaveImage, imageId={}", 
                                                java.time.LocalDateTime.now(), saveOperationId, imageId);
                                        Image savedImage = imageService.downloadAndSaveImage(imageId);
                                        
                                        if (savedImage != null) {
                                            log.info("[{}] [{}] [无元数据图片处理] 成功 - 图片已保存到数据库, imageId={}, dbId={}", 
                                                    java.time.LocalDateTime.now(), saveOperationId, imageId, savedImage.getId());
                                        } else {
                                            log.warn("[{}] [{}] [无元数据图片处理] 失败 - 图片保存返回null, imageId={}", 
                                                    java.time.LocalDateTime.now(), saveOperationId, imageId);
                                        }
                                    } catch (Exception e) {
                                        log.error("[{}] [{}] [无元数据图片处理] 异常 - 保存图片失败, imageId={}, error={}, stackTrace={}", 
                                                java.time.LocalDateTime.now(), saveOperationId, imageId, 
                                                e.getMessage(), org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace(e));
                                    }
                                }
                            }
                            log.info("[{}] [{}] [图片处理] 结束 - 完成处理", java.time.LocalDateTime.now(), saveOperationId);
                        }
                        
                        log.info("[{}] [{}] ========== 图片保存流程结束 ==========", java.time.LocalDateTime.now(), saveOperationId);

                        String imagesJson = null;
                        if (!imageIds[0].isEmpty()) {
                            java.util.List<ObjectNode> imageList = new java.util.ArrayList<>();
                            for (String imageId : imageIds[0]) {
                                ObjectNode imageNode = objectMapper.createObjectNode();
                                imageNode.put("image_id", imageId);
                                
                                // 尝试获取Nginx格式的URL
                                String nginxUrl = getNginxUrlForImage(imageId);
                                if (nginxUrl != null) {
                                    imageNode.put("url", nginxUrl);
                                } else {
                                    // 回退到旧的API格式
                                    imageNode.put("url", "/api/image/" + imageId + "/data");
                                }
                                
                                imageList.add(imageNode);
                            }
                            imagesJson = objectMapper.writeValueAsString(imageList);
                        }
                        
                        String sourcesJson = null;
                        if (!referenceList[0].isEmpty()) {
                            java.util.Map<String, Integer> docCountMap = new java.util.HashMap<>();
                            java.util.Map<String, String> docTypeMap = new java.util.HashMap<>();
                            java.util.Map<String, java.util.List<Integer>> docPageMap = new java.util.HashMap<>();
                            
                            for (ChatReference ref : referenceList[0]) {
                                String docName = ref.getDocName();
                                docCountMap.merge(docName, 1, Integer::sum);
                                if (ref.getDocType() != null) {
                                    docTypeMap.put(docName, ref.getDocType());
                                }
                                if (ref.getPage() != null) {
                                    docPageMap.computeIfAbsent(docName, k -> new java.util.ArrayList<Integer>()).add(ref.getPage());
                                }
                            }
                            
                            java.util.List<ObjectNode> sourcesList = new java.util.ArrayList<>();
                            for (java.util.Map.Entry<String, Integer> entry : docCountMap.entrySet()) {
                                ObjectNode sourceNode = objectMapper.createObjectNode();
                                sourceNode.put("docName", entry.getKey());
                                sourceNode.put("docType", docTypeMap.getOrDefault(entry.getKey(), ""));
                                sourceNode.put("referenceCount", entry.getValue());
                                
                                java.util.List<Integer> pages = docPageMap.get(entry.getKey());
                                if (pages != null && !pages.isEmpty()) {
                                    int minPage = pages.stream().mapToInt(Integer::intValue).min().orElse(0);
                                    int maxPage = pages.stream().mapToInt(Integer::intValue).max().orElse(0);
                                    if (minPage == maxPage) {
                                        sourceNode.put("pages", String.valueOf(minPage));
                                    } else {
                                        sourceNode.put("pages", minPage + "-" + maxPage);
                                    }
                                }
                                sourcesList.add(sourceNode);
                            }
                            sourcesJson = objectMapper.writeValueAsString(sourcesList);
                        }
                            
                        savedAssistantMessage[0] = chatService.saveAssistantMessage(
                            chatSessionId[0], request.getMessage(), finalAnswer, kbPathsJson, "rag", 1, durationMs, imagesJson, sourcesJson);
                    
                        if (savedAssistantMessage[0] != null) {
                            log.info("助手消息已保存: messageId={}, chatSessionId={}", savedAssistantMessage[0].getId(), chatSessionId[0]);

                            if (!referenceList[0].isEmpty()) {
                                chatService.saveReferences(savedAssistantMessage[0].getId(), referenceList[0]);
                                log.info("引用信息已保存: count={}, messageId={}", referenceList[0].size(), savedAssistantMessage[0].getId());
                            }
                        } else {
                            log.info("助手消息已存在或内容为空，跳过保存");
                        }

                        chatService.updateSessionSummary(chatSessionId[0],
                                "用户问: " + (request.getMessage().length() > 50
                                        ? request.getMessage().substring(0, 50) + "..."
                                        : request.getMessage()));

                    } catch (Exception e) {
                        log.error("保存聊天消息到数据库失败", e);
                    }
                } else {
                    log.error("跳过保存：finalAnswer 为空！fullAnswer.length={}, finalFullAnswer.length={}", 
                            fullAnswer.length(), finalFullAnswer[0].length());
                }

                try {
                    if (!emitterActive[0]) {
                        log.warn("SSE连接已断开，跳过发送finish事件");
                        return;
                    }
                    ObjectNode finishNode = objectMapper.createObjectNode();
                    finishNode.put("type", "finish");
                    finishNode.put("answer", finalAnswer);
                    finishNode.put("mode", "rag");
                    finishNode.put("session_id", sessionId);
                    finishNode.put("duration_ms", durationMs);

                    if (!referenceList[0].isEmpty()) {
                        java.util.List<ReferenceResponse> responseList = new java.util.ArrayList<>();
                        for (ChatReference ref : referenceList[0]) {
                            ReferenceResponse response = new ReferenceResponse();
                            response.setChunkId(ref.getChunkId());
                            response.setSource(ref.getDocName());
                            response.setDocType(ref.getDocType());
                            response.setPage(ref.getPage());
                            response.setPageEnd(ref.getPageEnd());
                            response.setPageRange(ref.getPageRange());
                            response.setSection(ref.getSection());
                            response.setChunkType(ref.getChunkType());
                            response.setSectionChunkId(ref.getSectionChunkId());
                            response.setPreview(ref.getPreview());
                            response.setBbox(ref.getBbox());
                            response.setBboxMode(ref.getBboxMode());
                            response.setScore(ref.getScore());
                            response.setExcerpt(ref.getExcerpt());
                            responseList.add(response);
                        }
                        finishNode.set("sources", objectMapper.valueToTree(responseList));
                    }

                    if (!citationList[0].isEmpty()) {
                        java.util.List<ReferenceResponse> citationResponseList = new java.util.ArrayList<>();
                        for (ChatReference ref : citationList[0]) {
                            ReferenceResponse response = new ReferenceResponse();
                            response.setChunkId(ref.getChunkId());
                            response.setSource(ref.getDocName());
                            response.setDocType(ref.getDocType());
                            response.setPage(ref.getPage());
                            response.setPageEnd(ref.getPageEnd());
                            response.setPageRange(ref.getPageRange());
                            response.setSection(ref.getSection());
                            response.setChunkType(ref.getChunkType());
                            response.setSectionChunkId(ref.getSectionChunkId());
                            response.setPreview(ref.getPreview());
                            response.setBbox(ref.getBbox());
                            response.setBboxMode(ref.getBboxMode());
                            response.setScore(ref.getScore());
                            response.setExcerpt(ref.getExcerpt());
                            citationResponseList.add(response);
                        }
                        finishNode.set("citations", objectMapper.valueToTree(citationResponseList));
                        log.info("finish事件包含citations: count={}", citationResponseList.size());
                    }

                    if (!imageIds[0].isEmpty()) {
                        java.util.List<ObjectNode> imageList = new java.util.ArrayList<>();
                        java.util.Set<String> processedImageIds = new java.util.HashSet<>();
                        for (java.util.Map<String, Object> metadata : imageMetadataList[0]) {
                            ObjectNode imageNode = objectMapper.createObjectNode();
                            imageNode.put("id", String.valueOf(metadata.get("id")));
                            imageNode.put("url", String.valueOf(metadata.get("url")));
                            if (metadata.containsKey("type") && metadata.get("type") != null) {
                                imageNode.put("type", String.valueOf(metadata.get("type")));
                            }
                            if (metadata.containsKey("source") && metadata.get("source") != null) {
                                imageNode.put("source", String.valueOf(metadata.get("source")));
                            }
                            if (metadata.containsKey("page") && metadata.get("page") != null) {
                                Object pageValue = metadata.get("page");
                                if (pageValue instanceof Number) {
                                    imageNode.put("page", ((Number) pageValue).intValue());
                                } else if (pageValue != null) {
                                    imageNode.put("page", pageValue.toString());
                                }
                            }
                            if (metadata.containsKey("description") && metadata.get("description") != null) {
                                imageNode.put("description", String.valueOf(metadata.get("description")));
                            }
                            if (metadata.containsKey("full_description") && metadata.get("full_description") != null) {
                                imageNode.put("full_description", String.valueOf(metadata.get("full_description")));
                            }
                            if (metadata.containsKey("score") && metadata.get("score") != null) {
                                Object scoreValue = metadata.get("score");
                                if (scoreValue instanceof Number) {
                                    imageNode.put("score", ((Number) scoreValue).doubleValue());
                                }
                            }
                            imageList.add(imageNode);
                            processedImageIds.add(String.valueOf(metadata.get("id")));
                        }
                        for (String imageId : imageIds[0]) {
                            if (!processedImageIds.contains(imageId)) {
                                ObjectNode imageNode = objectMapper.createObjectNode();
                                imageNode.put("id", imageId);
                                // 尝试获取Nginx格式的URL
                                String nginxUrl = imageService.getNginxUrlByImageId(imageId);
                                if (nginxUrl != null) {
                                    imageNode.put("url", nginxUrl);
                                } else {
                                    imageNode.put("url", "/api/image/" + imageId + "/data");
                                }
                                imageList.add(imageNode);
                            }
                        }
                        finishNode.set("images", objectMapper.valueToTree(imageList));
                        log.info("finish事件包含图片: count={}", imageList.size());
                    }

                    SseEmitter.SseEventBuilder finishEvent = SseEmitter.event()
                            .data(finishNode);
                    emitter.send(finishEvent);
                    log.info("📤 SSE finish事件已发送: answerLength={}, sourcesCount={}", finalAnswer.length(), referenceList[0].size());
                } catch (Exception e) {
                    log.error("发送finish事件失败", e);
                }

                try {
                    if (emitterActive[0]) {
                        emitter.complete();
                    }
                } catch (Exception e) {
                    log.warn("emitter.complete()失败，可能是连接已断开: {}", e.getMessage());
                }

            } catch (Exception e) {
                log.error("SSE流式处理失败", e);
                if (emitterActive[0]) {
                    try {
                        ObjectNode errorNode = objectMapper.createObjectNode();
                        errorNode.put("type", "error");
                        errorNode.put("message", e.getMessage());

                        SseEmitter.SseEventBuilder errorEvent = SseEmitter.event()
                                .name("error")
                                .data(errorNode);
                        emitter.send(errorEvent);
                    } catch (Exception ex) {
                        log.error("发送错误事件失败", ex);
                    }
                    try {
                        emitter.complete();
                    } catch (Exception ex) {
                        log.warn("emitter.complete()失败: {}", ex.getMessage());
                    }
                } else {
                    log.warn("SSE连接已断开，跳过发送错误事件");
                }
                try {
                    emitter.completeWithError(e);
                } catch (Exception ex) {
                    log.warn("emitter.completeWithError()失败: {}", ex.getMessage());
                }
            }
        });

        return emitter;
    }

    private ChatReference parseSourceToReference(JsonNode sourceItem) {
        ChatReference ref = new ChatReference();
        ref.setChunkId(sourceItem.has("chunk_id") ? sourceItem.get("chunk_id").asText() : "");
        ref.setDocName(sourceItem.has("source") ? sourceItem.get("source").asText() : "");
        
        JsonNode pageNode = sourceItem.path("page");
        ref.setPage(!pageNode.isNull() ? pageNode.asInt() : null);
        
        JsonNode pageEndNode = sourceItem.path("page_end");
        ref.setPageEnd(!pageEndNode.isNull() ? pageEndNode.asInt() : null);
        
        ref.setPageRange(sourceItem.has("page_range") ? sourceItem.get("page_range").asText() : null);
        ref.setSection(sourceItem.has("section") ? sourceItem.get("section").asText() : null);
        ref.setChunkType(sourceItem.has("chunk_type") ? sourceItem.get("chunk_type").asText() : null);
        ref.setDocType(sourceItem.has("doc_type") ? sourceItem.get("doc_type").asText() : null);
        
        JsonNode sectionChunkIdNode = sourceItem.path("section_chunk_id");
        ref.setSectionChunkId(!sectionChunkIdNode.isNull() ? sectionChunkIdNode.asInt() : null);
        
        ref.setPreview(sourceItem.has("preview") ? sourceItem.get("preview").asText() : null);
        ref.setExcerpt(sourceItem.has("preview") ? sourceItem.get("preview").asText() : 
                (sourceItem.has("content") ? sourceItem.get("content").asText() : 
                (sourceItem.has("excerpt") ? sourceItem.get("excerpt").asText() : "")));
        if (sourceItem.has("score")) {
            JsonNode scoreNode = sourceItem.get("score");
            if (scoreNode.isNumber()) {
                ref.setScore(BigDecimal.valueOf(scoreNode.asDouble()));
            }
        }
        return ref;
    }

    private ChatReference parseCitationToReference(JsonNode citationItem) {
        ChatReference ref = new ChatReference();
        ref.setChunkId(citationItem.has("chunk_id") ? citationItem.get("chunk_id").asText() : "");
        ref.setDocName(citationItem.has("source") ? citationItem.get("source").asText() : "");
        ref.setDocType(citationItem.has("doc_type") ? citationItem.get("doc_type").asText() : null);
        ref.setSection(citationItem.has("section") ? citationItem.get("section").asText() : null);
        ref.setPreview(citationItem.has("preview") ? citationItem.get("preview").asText() : null);
        
        JsonNode pageNode = citationItem.path("page");
        ref.setPage(!pageNode.isNull() ? pageNode.asInt() : null);
        
        JsonNode pageEndNode = citationItem.path("page_end");
        ref.setPageEnd(!pageEndNode.isNull() ? pageEndNode.asInt() : null);
        
        JsonNode sectionChunkIdNode = citationItem.path("section_chunk_id");
        ref.setSectionChunkId(!sectionChunkIdNode.isNull() ? sectionChunkIdNode.asInt() : null);
        
        if (citationItem.has("bbox")) {
            ref.setBbox(citationItem.get("bbox").toString());
        }
        ref.setBboxMode(citationItem.has("bbox_mode") ? citationItem.get("bbox_mode").asText() : null);
        ref.setExcerpt(citationItem.has("preview") ? citationItem.get("preview").asText() : "");
        if (citationItem.has("score")) {
            JsonNode scoreNode = citationItem.get("score");
            if (scoreNode.isNumber()) {
                ref.setScore(BigDecimal.valueOf(scoreNode.asDouble()));
            }
        }
        return ref;
    }

    /**
     * 根据imageId获取Nginx访问格式的URL
     */
    private String getNginxUrlForImage(String imageId) {
        try {
            // 从数据库查询图片记录
            Image image = imageService.getImageByImageId(imageId);
            
            // 如果没找到，尝试带.jpg后缀
            if (image == null && !imageId.toLowerCase().endsWith(".jpg")) {
                image = imageService.getImageByImageId(imageId + ".jpg");
            }
            
            // 找到图片记录，提取文件名并构造Nginx URL
            if (image != null && image.getPath() != null) {
                String dbPath = image.getPath();
                log.debug("从数据库获取到图片路径: imageId={}, path={}", imageId, dbPath);
                
                // 从路径中提取文件名
                String filename = extractFilenameFromPath(dbPath);
                if (filename != null) {
                    // 构造Nginx URL: /files/img/{filename}
                    String nginxUrl = imageUrlPrefix + "img/" + filename;
                    log.info("图片URL已转换: imageId={}, nginxUrl={}", imageId, nginxUrl);
                    return nginxUrl;
                }
            }
            
            log.warn("无法从数据库获取图片信息，将使用旧格式URL: imageId={}", imageId);
        } catch (Exception e) {
            log.warn("获取图片Nginx URL失败: imageId={}, error={}", imageId, e.getMessage());
        }
        
        return null;
    }

    /**
     * 从文件路径中提取文件名
     * 支持格式: /app/uploads/img/xxx.jpg 或 /opt/deploy/uploads/img/xxx.jpg
     */
    private String extractFilenameFromPath(String path) {
        if (path == null || path.isEmpty()) {
            return null;
        }
        
        try {
            // 使用Paths处理路径，兼容不同操作系统的分隔符
            Path filePath = Paths.get(path);
            return filePath.getFileName().toString();
        } catch (Exception e) {
            log.debug("从路径提取文件名失败: path={}, error={}", path, e.getMessage());
            
            // 备用方案：字符串分割
            try {
                // 查找最后一个 / 或 \
                int lastSlash = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'));
                if (lastSlash >= 0 && lastSlash < path.length() - 1) {
                    return path.substring(lastSlash + 1);
                }
            } catch (Exception ex) {
                log.debug("备用方案提取文件名也失败: {}", ex.getMessage());
            }
        }
        
        return null;
    }
}
