package com.rj.service.impl;

import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.aigc.generation.GenerationUsage;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rj.common.AudioAnalysisSceneType;
import com.rj.common.LocalLlmSummaryResult;
import com.rj.entity.*;
import com.rj.service.*;
import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
import dev.langchain4j.model.openai.OpenAiStreamingChatModel;
import dev.langchain4j.model.output.TokenUsage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import jakarta.annotation.PostConstruct;

import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 音频文本分析 - 大模型通用服务实现
 */
@Service
@Slf4j
public class AudioTextAnalysisLlmServiceImpl implements IAudioTextAnalysisLlmService {

    /** LangChain4j 流式调用结束后的文本与（可选）服务端 usage */
    private record LocalChatOutcome(String text, TokenUsage tokenUsage) {}

    private static final Map<String, String> SYSTEM_PROMPT_CACHE = new ConcurrentHashMap<>();
    private static final Map<String, String> USER_PROMPT_CACHE = new ConcurrentHashMap<>();

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private IAudioTextAnalysisFurnitureService furnitureService;

    @Autowired
    private IAudioManagementService audioManagementService;

    @Autowired
    private ITodoItemService todoItemService;

    @Autowired
    private IAudioTextAnalysisSopService sopService;

    @Value("${ai.qa.local.chat-url:http://192.168.1.44:8000/v1/chat/completions}")
    private String chatCompletionsUrl;

    @Value("${ai.qa.local.default-model:Qwen2.5-7B-Instruct}")
    private String localDefaultModel;

    /** 单次补全上限（OpenAI max_tokens）；与「模型总上下文 context-length」不是同一概念 */
    @Value("${ai.qa.local.max-tokens:2048}")
    private int localMaxTokens;

    /** 本地模型上下文长度（与推理侧 max context 一致，用于避免 input+max_tokens 超过上限；默认 16384） */
    @Value("${ai.qa.local.context-length:16384}")
    private int localContextLength;

    /** 预留余量，避免服务端计数与本地估算不一致 */
    @Value("${ai.qa.local.token-margin:80}")
    private int localTokenMargin;

    /**
     * 截断转写时希望保留的「最小输出预算」启发值（须显著小于 context-length，且不得大于 max-tokens，
     * 因为实际 effMax 恒为 min(max-tokens, 剩余上下文)）。
     */
    @Value("${ai.qa.local.min-output-tokens:128}")
    private int configuredLocalMinOutputTokens;

    /** {@link #configuredLocalMinOutputTokens} 经与 max-tokens 对齐后的实际取值 */
    private int effectiveLocalMinOutputTokens;

    /**
     * 对完整 prompt 字符串做输入 token 的保守上界估算（偏大会多截断、偏少仍可能 400）。
     * 中文为主可保持默认；英文偏多时可酌情调低（如 0.35）。
     */
    @Value("${ai.qa.local.input-tokens-per-char:1.25}")
    private double localInputTokensPerChar;

    @PostConstruct
    void clampLocalMinOutputTokens() {
        effectiveLocalMinOutputTokens = Math.max(1, Math.min(configuredLocalMinOutputTokens, localMaxTokens));
        if (effectiveLocalMinOutputTokens < configuredLocalMinOutputTokens) {
            log.warn(
                    "ai.qa.local.min-output-tokens={} 大于 ai.qa.local.max-tokens={}：有效输出 effMax 恒 ≤ max-tokens，已钳制为 {}。"
                            + " 切勿将 min-output-tokens 配成与 context-length 相同。",
                    configuredLocalMinOutputTokens,
                    localMaxTokens,
                    effectiveLocalMinOutputTokens);
        }
    }

    @Autowired
    IAiPromptsService aiPromptsService;

    @Override
    public GenerationResult generateSummaryByLLM(AudioAnalysisSceneType sceneType, String recordingText) {
        long startTime = System.currentTimeMillis();
        log.info("开始调用大模型生成总结，场景: {}", sceneType);

        AiPrompts sysTotalItemPrompts = aiPromptsService.listByBizKeys(sceneType.getSceneName(), "total_item", "systemPrompt", "total_item").get(0);
        AiPrompts userTotalItemPrompts = aiPromptsService.listByBizKeys(sceneType.getSceneName(), "total_item", "userPrompt", "total_item").get(0);
        List<AiPrompts> userFieldsItemPrompts = aiPromptsService.listByBizKeys(sceneType.getSceneName(), "fields", "userPrompt", null);
        AiPrompts userReplyStructureRequirementsItemPrompts = aiPromptsService.listByBizKeys(sceneType.getSceneName(), "reply_tructure_requirements", "userPrompt", null).get(0);
        AiPrompts userTecordingTextItemPrompts = aiPromptsService.listByBizKeys(sceneType.getSceneName(), "recording_text", "userPrompt", null).get(0);

        StringBuilder userItemPrompts = new StringBuilder();
        userItemPrompts.append(userTotalItemPrompts.getPromptText()).append("\n");
        int i=0;
        for (AiPrompts userFieldItemPrompt : userFieldsItemPrompts) {
            userItemPrompts.append((++i)  +",  \""+userFieldItemPrompt.getFieldCode()+"\":"+ "\""+userFieldItemPrompt.getPromptText()+"\";").append("\n");
        }

        userItemPrompts.append(userReplyStructureRequirementsItemPrompts.getPromptText()).append("\n");

        userItemPrompts.append(userTecordingTextItemPrompts.getPromptText());

        String userPrompt = buildPrompt(userItemPrompts.toString(), recordingText);
       log.info("userPrompt------------------------------\n: {}", userPrompt);
        log.info("total  length: recordingText------------------------------ \n: {}:{}", userPrompt.length() ,recordingText.length());
        Generation gen = new Generation();
        Message systemMsg = Message.builder()
                .role(Role.SYSTEM.getValue())
                .content(sysTotalItemPrompts.getPromptText())
                .build();
        Message userMsg = Message.builder()
                .role(Role.USER.getValue())
                .content(userPrompt)
                .build();

        GenerationParam param = GenerationParam.builder()
                .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                .model(sceneType.getModelName())
                .messages(Arrays.asList(systemMsg, userMsg))
                .resultFormat(GenerationParam.ResultFormat.MESSAGE)
                .build();

        try {
            GenerationResult call = gen.call(param);
            String rawContent = call.getOutput().getChoices().get(0).getMessage().getContent();
            log.info("大模型生成总结完成，场景: {}, 输入长度：{},结果长度: {}", sceneType, recordingText.length(),rawContent != null ? rawContent.length() : 0);

            return call;
        } catch (NoApiKeyException e) {
            log.error("API密钥未配置", e);
            throw new RuntimeException("API密钥未配置: " + e.getMessage(), e);
        } catch (InputRequiredException e) {
            log.error("输入参数错误", e);
            throw new RuntimeException("输入参数错误: " + e.getMessage(), e);
        } catch (Exception e) {
            log.error("调用大模型生成总结失败, 场景: {}", sceneType, e);
            throw new RuntimeException("调用大模型失败: " + e.getMessage(), e);
        } finally {
            long endTime = System.currentTimeMillis();
            long durationMillis = endTime - startTime;
            double durationSeconds = durationMillis / 1000.0;
            double durationMinutes = durationSeconds / 60.0;
            log.info("调用大模型耗时: {} 毫秒, {} 秒, {} 分钟, 场景: {}",
                    durationMillis,
                    String.format("%.2f", durationSeconds),
                    String.format("%.2f", durationMinutes),
                    sceneType);
        }
    }

    @Override
    public LocalLlmSummaryResult generateSummaryByLocalLLM(AudioAnalysisSceneType sceneType, String recordingText) {
        long startTime = System.currentTimeMillis();
        log.info("开始调用本地大模型生成总结，场景: {}", sceneType);

        String systemPrompt = getSystemPrompt(sceneType);
        String userPromptTemplate = getUserPromptTemplate(sceneType);
        String recordingForPrompt = fitRecordingTextToLocalContext(systemPrompt, userPromptTemplate, recordingText);
        String userPrompt = buildPrompt(userPromptTemplate, recordingForPrompt);

        try {
            int effectiveMaxTokens = computeEffectiveLocalMaxTokens(systemPrompt, userPrompt);
            LocalChatOutcome outcome = callLocalOpenAiChatCompletionsByLangChain4j(
                    systemPrompt,
                    userPrompt,
                    localDefaultModel,
                    effectiveMaxTokens);
            String rawContent = outcome.text();
            TokenUsage usage = outcome.tokenUsage();
            Integer total = usage != null ? usage.totalTokenCount() : null;
            Integer input = usage != null ? usage.inputTokenCount() : null;
            Integer output = usage != null ? usage.outputTokenCount() : null;
            log.info(
                    "本地大模型生成总结完成，token花费，总token: {}, 输入token: {}, 输出token: {}, 输出token详情: {}",
                    total,
                    input,
                    output,
                    usage != null ? usage.toString() : "无");
            log.info("本地大模型生成总结完成，场景: {}, 结果长度: {}",
                    sceneType, rawContent != null ? rawContent.length() : 0);
            return new LocalLlmSummaryResult(rawContent, total, input, output);
        } catch (Exception e) {
            log.error("调用本地大模型生成总结失败, 场景: {}", sceneType, e);
            throw new RuntimeException("调用本地大模型失败: " + e.getMessage(), e);
        } finally {
            long endTime = System.currentTimeMillis();
            long durationMillis = endTime - startTime;
            double durationSeconds = durationMillis / 1000.0;
            double durationMinutes = durationSeconds / 60.0;
            log.info("调用本地大模型耗时: {} 毫秒, {} 秒, {} 分钟, 场景: {}",
                    durationMillis,
                    String.format("%.2f", durationSeconds),
                    String.format("%.2f", durationMinutes),
                    sceneType);
        }
    }

    /**
     * 与 {@link AiQaCustomerAskServiceImpl} 中非流式问答路径一致：LangChain4j + OpenAI 兼容接口，流式聚合为完整文本。
     */
    private LocalChatOutcome callLocalOpenAiChatCompletionsByLangChain4j(
            String systemPrompt, String userContent, String model, int maxTokens) {
        log.info("音频分析本地 LLM：model={}, chatCompletionsUrl={}, maxTokens={}", model, chatCompletionsUrl,maxTokens);
        String openAiBaseUrl = normalizeOpenAiBaseUrl(chatCompletionsUrl);
        OpenAiStreamingChatModel chatModel = OpenAiStreamingChatModel.builder()
                .baseUrl(openAiBaseUrl)
                .apiKey("not-used")
                .modelName(model)
                .maxTokens(maxTokens)
                .build();

        String prompt = buildFullLocalLlmPrompt(systemPrompt, userContent);
        StringBuilder answerBuffer = new StringBuilder();
        CountDownLatch done = new CountDownLatch(1);
        AtomicReference<Throwable> errorRef = new AtomicReference<>();
        AtomicReference<TokenUsage> usageRef = new AtomicReference<>();

        chatModel.chat(prompt, new StreamingChatResponseHandler() {
            @Override
            public void onPartialResponse(String partialResponse) {
                if (partialResponse != null && !partialResponse.isEmpty()) {
                    answerBuffer.append(partialResponse);
                }
            }

            @Override
            public void onCompleteResponse(ChatResponse completeResponse) {
                if (completeResponse != null) {
                    usageRef.set(completeResponse.tokenUsage());
                }
                done.countDown();
            }

            @Override
            public void onError(Throwable error) {
                errorRef.set(error);
                done.countDown();
            }
        });

        try {
            boolean finished = done.await(120, TimeUnit.SECONDS);
            if (!finished) {
                throw new IllegalStateException("本地大模型流式调用超时（120s）");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new IllegalStateException("本地大模型流式调用被中断", e);
        }

        if (errorRef.get() != null) {
            throw new IllegalStateException("本地大模型流式调用失败: " + errorRef.get().getMessage(), errorRef.get());
        }

        String answer = answerBuffer.toString();
        if (answer.isEmpty()) {
            throw new IllegalStateException("本地大模型流式调用返回空响应");
        }
        return new LocalChatOutcome(answer, usageRef.get());
    }

    private String normalizeOpenAiBaseUrl(String rawUrl) {
        if (rawUrl == null || rawUrl.trim().isEmpty()) {
            throw new IllegalArgumentException("ai.qa.local.chat-url 不能为空");
        }
        String normalized = rawUrl.trim();
        while (normalized.endsWith("/")) {
            normalized = normalized.substring(0, normalized.length() - 1);
        }
        String suffix = "/chat/completions";
        if (normalized.endsWith(suffix)) {
            normalized = normalized.substring(0, normalized.length() - suffix.length());
        }
        return normalized;
    }

    private String buildFullLocalLlmPrompt(String systemPrompt, String userContent) {
        return "系统指令：" + systemPrompt + "\n\n用户输入：" + userContent;
    }

    private int estimateLocalPromptInputTokens(String fullPrompt) {
        if (fullPrompt == null || fullPrompt.isEmpty()) {
            return 0;
        }
        return (int) Math.ceil(fullPrompt.length() * localInputTokensPerChar);
    }

    private int computeEffectiveLocalMaxTokens(String systemPrompt, String userPrompt) {
        String full = buildFullLocalLlmPrompt(systemPrompt, userPrompt);
        int estIn = estimateLocalPromptInputTokens(full);
        int capByContext = localContextLength - localTokenMargin - estIn;
        int effective = Math.min(localMaxTokens, capByContext);
        return Math.max(1, effective);
    }

    /**
     * 在不超过本地模型上下文的前提下，尽量保留更多转写文本；必要时从尾部截断。
     */
    private String fitRecordingTextToLocalContext(
            String systemPrompt, String userPromptTemplate, String recordingText) {
        String recording = recordingText != null ? recordingText : "";
        if (recording.isEmpty()) {
            return recording;
        }
        String userFull = buildPrompt(userPromptTemplate, recording);
        String full = buildFullLocalLlmPrompt(systemPrompt, userFull);
        int est = estimateLocalPromptInputTokens(full);
        int effMax = Math.min(localMaxTokens, localContextLength - localTokenMargin - est);
        if (effMax >= effectiveLocalMinOutputTokens) {
            return recording;
        }
        int lo = 0;
        int hi = recording.length();
        int best = 0;
        while (lo <= hi) {
            int mid = (lo + hi) >>> 1;
            String sub = recording.substring(0, mid);
            userFull = buildPrompt(userPromptTemplate, sub);
            full = buildFullLocalLlmPrompt(systemPrompt, userFull);
            est = estimateLocalPromptInputTokens(full);
            effMax = Math.min(localMaxTokens, localContextLength - localTokenMargin - est);
            if (effMax >= effectiveLocalMinOutputTokens) {
                best = mid;
                lo = mid + 1;
            } else {
                hi = mid - 1;
            }
        }
        if (best >= recording.length()) {
            return recording;
        }
        if (best <= 0) {
            userFull = buildPrompt(userPromptTemplate, "");
            full = buildFullLocalLlmPrompt(systemPrompt, userFull);
            int estEmpty = estimateLocalPromptInputTokens(full);
            int effMaxEmpty = Math.min(localMaxTokens, localContextLength - localTokenMargin - estEmpty);
            log.warn(
                    "本地 LLM 上下文过紧：无转写时估算剩余输出预算 effMax≈{}（max-tokens={}），仍小于 effective min-output-tokens={}"
                            + "（配置 min-output-tokens={}）。请减小提示词、提高 context-length，或降低 min-output-tokens / 提高 max-tokens。将仍尝试调用。",
                    effMaxEmpty,
                    localMaxTokens,
                    effectiveLocalMinOutputTokens,
                    configuredLocalMinOutputTokens);
            return "";
        }
        log.warn(
                "录音转写过长，已按本地模型上下文截断：原长度 {} 字符，保留 {} 字符（context-length={}，max-tokens={}）",
                recording.length(),
                best,
                localContextLength,
                localMaxTokens);
        return recording.substring(0, best);
    }

    private String buildPrompt(String template, String recordingText) {
        if (template == null) {
            return recordingText != null ? recordingText : "";
        }
        return template.replace("{RECORDING_TEXT}", recordingText != null ? recordingText : "");
    }

    private String getSystemPrompt(AudioAnalysisSceneType sceneType) {
        return SYSTEM_PROMPT_CACHE.computeIfAbsent(sceneType.name(), key -> {
            String path = sceneType.getSystemPromptPath();
            String content = readResourceFile(path);
            if (content == null || content.trim().isEmpty()) {
                log.warn("无法读取系统提示词文件: {}，场景: {}，请检查资源文件是否存在", path, sceneType);
            }
            return content != null ? content : "";
        });
    }

    private String getUserPromptTemplate(AudioAnalysisSceneType sceneType) {
        return USER_PROMPT_CACHE.computeIfAbsent(sceneType.name(), key -> {
            String path = sceneType.getUserPromptPath();
            String content = readResourceFile(path);
            if (content == null || content.trim().isEmpty()) {
                log.warn("无法读取用户提示词模板文件: {}，场景: {}，请检查资源文件是否存在", path, sceneType);
            }
            return content != null ? content : "";
        });
    }

    private String readResourceFile(String resourcePath) {
        try {
            ClassLoader classLoader = getClass().getClassLoader();
            InputStream inputStream = classLoader.getResourceAsStream(resourcePath);
            if (inputStream == null) {
                log.error("无法找到资源文件: {}", resourcePath);
                return null;
            }
            try (Scanner scanner = new Scanner(inputStream, StandardCharsets.UTF_8.name()).useDelimiter("\\A")) {
                String content = scanner.hasNext() ? scanner.next() : "";
                log.info("成功读取提示词文件: {}, 长度: {}", resourcePath, content.length());
                return content;
            }
        } catch (Exception e) {
            log.error("读取资源文件失败: {}", resourcePath, e);
            return null;
        }
    }

    /**
     * 处理完整的业务逻辑：调用大模型生成总结，解析JSON，保存数据
     * 此方法用于家具场景的完整业务处理
     *
     * @param sceneType     业务场景类型
     * @param recordingText 录音转写文本
     * @param parentId      父ID（AudioManagement的ID）
     * @param ownerName     所属人姓名
     * @param ownerPhone    所属人电话
     * @param customerName  客户姓名
     * @param customerPhone 客户电话
     * @return 保存后的AudioTextAnalysisFurniture对象，如果处理失败返回null
     */
    @Override
    public AudioTextAnalysisFurniture generateSummaryAndSave(
            AudioAnalysisSceneType sceneType,
            String recordingText,
            String parentId,
            String ownerName,
            String ownerPhone,
            String customerName,
            String customerPhone) {
        try {
            GenerationResult generationResult = generateSummaryByLLM(sceneType, recordingText);
            log.info("大模型生成总结完成，token花费，总token: {}, 输入token: {}, 输出token: {}, 输出token详情: {}",
                    generationResult.getUsage().getTotalTokens(),generationResult.getUsage().getInputTokens(),generationResult.getUsage().getOutputTokens(),generationResult.getUsage().getOutputTokensDetails());
            // 1. 调用大模型生成总结
            String rawContent = generationResult.getOutput().getChoices().get(0).getMessage().getContent();
            if (rawContent == null || rawContent.trim().isEmpty()) {
                log.warn("大模型返回内容为空");
                return null;
            }

            // 2. 创建AudioTextAnalysisFurniture对象
            AudioTextAnalysisFurniture furniture = new AudioTextAnalysisFurniture();
            furniture.setRecordingText(recordingText);

            // 3. 解析JSON并填充对象
            applyStructuredResult(furniture, rawContent, parentId, ownerName, ownerPhone, customerName, customerPhone);

            // 4. 更新AudioManagement的summary与 token 用量
            AudioManagement audioManagement = new AudioManagement();
            audioManagement.setId(parentId);
            audioManagement.setSummary(rawContent);
            applyDashScopeUsageToAudioManagement(audioManagement, generationResult);
            audioManagementService.updateById(audioManagement);

            // 5. 保存或更新AudioTextAnalysisFurniture记录
            LambdaQueryWrapper<AudioTextAnalysisFurniture> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(AudioTextAnalysisFurniture::getParentId, parentId);
            AudioTextAnalysisFurniture existing = furnitureService.getOne(wrapper);
            LocalDateTime now = LocalDateTime.now();
            if (existing != null) {
                furniture.setId(existing.getId());
                // 更新记录：只设置更新时间，不修改创建时间
                furniture.setUpdatedAt(now);
            } else {
                // 新增记录：需要设置创建时间和更新时间
                furniture.setCreatedAt(now);
                furniture.setUpdatedAt(now);
            }

            furniture.setParentId(parentId);
            furniture.setRecordingText(null); // 保存时清除recordingText
            furniture.setOwnerPhone(ownerPhone);
            furniture.setCustomerPhone(customerPhone);
            boolean success = furnitureService.saveOrUpdate(furniture);
            if (success) {
                log.info("保存AudioTextAnalysisFurniture成功，ID: {}, parentId: {}", furniture.getId(), parentId);
                return furniture;
            } else {
                log.warn("保存AudioTextAnalysisFurniture失败，parentId: {}", parentId);
                return null;
            }
        } catch (Exception e) {
            log.error("生成总结并保存失败，parentId: {}", parentId, e);
            throw new RuntimeException("生成总结并保存失败: " + e.getMessage(), e);
        }
    }

    @Override
    public AudioTextAnalysisFurniture generateSummaryAndSave_V2(
            AudioAnalysisSceneType sceneType,
            String recordingText,
            String parentId,
            String ownerName,
            String ownerPhone,
            String customerName,
            String customerPhone) {
        LocalLlmSummaryResult llmResult = generateSummaryByLocalLLM(sceneType, recordingText);
        String rawContent = llmResult.rawContent();
        if (rawContent == null || rawContent.trim().isEmpty()) {
            log.warn("大模型返回内容为空");
            return null;
        }
        AudioTextAnalysisFurniture furniture = new AudioTextAnalysisFurniture();
        AudioManagement audioManagement = new AudioManagement();
        audioManagement.setId(parentId);
        audioManagement.setSummary(rawContent);
        applyLocalLlmTokenCountsToAudioManagement(audioManagement, llmResult);
        audioManagementService.updateById(audioManagement);

        return furniture;
    }
    @Autowired
    private com.rj.service.IAudioManagementSegmentsService audioManagementSegmentsService;

    @Override
    public AudioTextAnalysisFurniture generateSummaryAndSaveForDetail(
            AudioAnalysisSceneType sceneType,
            String recordingText,
            String parentId,
            String ownerName,
            String ownerPhone,
            String customerName,
            String customerPhone) {

                LambdaQueryWrapper<AudioManagementSegments> segQuery = new LambdaQueryWrapper<>();
                segQuery.eq(AudioManagementSegments::getParentId, parentId)
                        .orderByAsc(AudioManagementSegments::getChunkIndex);
                List<AudioManagementSegments> segmentList = audioManagementSegmentsService.list(segQuery);
                int  emptyCount = 0 ;
                StringBuilder mergedText = new StringBuilder();
                if (segmentList != null && !segmentList.isEmpty()) {
                    for (AudioManagementSegments seg : segmentList) {
                        String segText = seg.getRecordingText();
                        if (segText != null && !segText.trim().isEmpty()) {
                            if (mergedText.length() > 0) {
                                mergedText.append("\n");
                            }
                            mergedText.append(segText.trim());
                        }else {
                            emptyCount ++;
                        }
                    }
                }
                log.info(" AudioManagementSegments 总数是：  {} ，未转文本记录条数是： {}",segmentList.size(), emptyCount);


        GenerationResult generationResult = generateSummaryByLLM(sceneType, mergedText.toString());
        log.info("大模型生成总结完成，token花费，总token: {}, 输入token: {}, 输出token: {}, 输出token详情: {}",
                generationResult.getUsage().getTotalTokens(),generationResult.getUsage().getInputTokens(),generationResult.getUsage().getOutputTokens(),generationResult.getUsage().getOutputTokensDetails());
        // 1. 调用大模型生成总结
        String rawContent = generationResult.getOutput().getChoices().get(0).getMessage().getContent();
        if (rawContent == null || rawContent.trim().isEmpty()) {
            log.warn("大模型返回内容为空");
            return null;
        }
        AudioTextAnalysisFurniture furniture = new AudioTextAnalysisFurniture();
        AudioManagement audioManagement = new AudioManagement();
        audioManagement.setId(parentId);
        audioManagement.setSummary(rawContent);
//        applyLocalLlmTokenCountsToAudioManagement(audioManagement, llmResult);
        audioManagementService.updateById(audioManagement);

        return furniture;
    }

    private void applyDashScopeUsageToAudioManagement(AudioManagement target, GenerationResult result) {
        if (result == null || result.getUsage() == null) {
            return;
        }
        GenerationUsage usage = result.getUsage();
        target.setTotalTokens(usage.getTotalTokens());
        target.setInputTokens(usage.getInputTokens());
        target.setOutputTokens(usage.getOutputTokens());
    }

    private void applyLocalLlmTokenCountsToAudioManagement(AudioManagement target, LocalLlmSummaryResult result) {
        if (result == null) {
            return;
        }
        if (result.totalTokens() != null) {
            target.setTotalTokens(result.totalTokens());
        }
        if (result.inputTokens() != null) {
            target.setInputTokens(result.inputTokens());
        }
        if (result.outputTokens() != null) {
            target.setOutputTokens(result.outputTokens());
        }
    }

    /**
     * 解析大模型返回的JSON结果并填充到AudioTextAnalysisFurniture对象
     *
     * @param furniture     家具意向分析对象
     * @param rawContent    大模型返回的原始内容
     * @param parentId      父ID
     * @param ownerName     所属人姓名
     * @param ownerPhone    所属人电话
     * @param customerName  客户姓名
     * @param customerPhone 客户电话
     */
    private void applyStructuredResult(
            AudioTextAnalysisFurniture furniture,
            String rawContent,
            String parentId,
            String ownerName,
            String ownerPhone,
            String customerName,
            String customerPhone) {
        String normalized = normalizeJson(rawContent);
        try {
            JsonNode root = objectMapper.readTree(normalized);

            // 基础字段
            furniture.setCustomerType(textValue(root, "customer_type"));
            furniture.setFamilyStructure(textValue(root, "family_structure"));
            furniture.setIntentionProducts(textValue(root, "intention_products"));
            furniture.setSummarySentence(textValue(root, "summary"));
            furniture.setDecorationStyle(textValue(root, "decoration_style"));

            // 产品维度字段，每个都是JSON对象，需要转换为JSON字符串
            furniture.setSofa(jsonObjectToString(root, "sofa"));
            furniture.setTeaTable(jsonObjectToString(root, "tea_table"));
            furniture.setDiningTable(jsonObjectToString(root, "dining_table"));
            furniture.setStudyDesk(jsonObjectToString(root, "study_desk"));
            furniture.setTvCabinet(jsonObjectToString(root, "tv_cabinet"));
            furniture.setCabinet(jsonObjectToString(root, "cabinet"));
            furniture.setWineCabinet(jsonObjectToString(root, "wine_cabinet"));
            furniture.setMasterBedroomCabinet(jsonObjectToString(root, "master_bedroom_cabinet"));
            furniture.setSecondaryBedroomCabinet(jsonObjectToString(root, "secondary_bedroom_cabinet"));
            furniture.setShoeCabinet(jsonObjectToString(root, "shoe_cabinet"));
            furniture.setBedAndMattress(jsonObjectToString(root, "bed_and_mattress"));
            furniture.setSummary1(jsonObjectToString(root, "summary1"));
            furniture.setSummary2(jsonObjectToString(root, "summary2"));
            furniture.setSummary3(jsonObjectToString(root, "summary3"));

            // 解析并保存待办事项
            saveTodoItems(root, parentId, ownerName, ownerPhone);

            // 解析并保存SOP评分信息
            saveSopInfo(root, parentId, ownerName, ownerPhone, customerName, customerPhone);
        } catch (JsonProcessingException e) {
            log.warn("解析大模型返回JSON失败，使用原始内容作为总结: {}", e.getMessage());
            furniture.setSummarySentence(rawContent);
        }
    }

    /**
     * 规范化JSON字符串（去除markdown代码块标记等）
     */
    private String normalizeJson(String content) {
        if (content == null) {
            return "";
        }
        String trimmed = content.trim();
        if (trimmed.startsWith("```")) {
            int firstLineBreak = trimmed.indexOf('\n');
            int lastFence = trimmed.lastIndexOf("```");
            if (firstLineBreak >= 0 && lastFence > firstLineBreak) {
                trimmed = trimmed.substring(firstLineBreak + 1, lastFence).trim();
            }
        }
        return trimmed;
    }

    /**
     * 从JSON节点中获取文本值
     */
    private String textValue(JsonNode root, String field) {
        JsonNode node = root.get(field);
        return node == null || node.isNull() ? null : node.asText();
    }

    /**
     * 将JSON对象节点转换为JSON字符串
     */
    private String jsonObjectToString(JsonNode root, String field) {
        JsonNode node = root.get(field);
        if (node == null || node.isNull()) {
            return null;
        }
        try {
            if (node.isObject()) {
                return objectMapper.writeValueAsString(node);
            } else if (node.isTextual()) {
                return node.asText();
            } else {
                return objectMapper.writeValueAsString(node);
            }
        } catch (JsonProcessingException e) {
            log.warn("转换JSON对象失败，字段: {}, 错误: {}", field, e.getMessage());
            return node.isTextual() ? node.asText() : node.toString();
        }
    }

    /**
     * 解析并保存待办事项
     */
    private void saveTodoItems(JsonNode root, String parentId, String ownerName, String ownerPhone) {
        try {
            JsonNode todoItemsNode = root.get("todo_item");
            if (todoItemsNode == null || !todoItemsNode.isArray()) {
                log.info("未找到待办事项数组或格式不正确");
                return;
            }

            LocalDateTime now = LocalDateTime.now();
            int savedCount = 0;

            for (JsonNode todoItemNode : todoItemsNode) {
                if (!todoItemNode.isObject()) {
                    log.warn("待办事项项不是JSON对象，跳过");
                    continue;
                }

                String todoTitle = textValue(todoItemNode, "todo_title");
                String todoDetail = textValue(todoItemNode, "todo_detail");

                // 如果标题和详情都为空，跳过
                if ((todoTitle == null || todoTitle.trim().isEmpty()) &&
                        (todoDetail == null || todoDetail.trim().isEmpty())) {
                    log.warn("待办事项标题和详情都为空，跳过");
                    continue;
                }

                TodoItem todoItem = new TodoItem();
                todoItem.setId(UUID.randomUUID().toString());
                todoItem.setParentId(parentId);
                todoItem.setTodoTitle(todoTitle != null ? todoTitle.trim() : "");
                todoItem.setTodoDetail(todoDetail != null ? todoDetail.trim() : "");
                todoItem.setOwnerName(ownerName);
                todoItem.setOwnerPhone(ownerPhone);
                todoItem.setStatus("待处理");
                todoItem.setCreateTime(now);
                todoItem.setUpdateTime(now);
                todoItem.setPendingDate(now.plusDays(3));

                boolean success = todoItemService.save(todoItem);
                if (success) {
                    savedCount++;
                    log.info("保存待办事项成功，ID: {}, 标题: {}", todoItem.getId(), todoItem.getTodoTitle());
                } else {
                    log.warn("保存待办事项失败，标题: {}", todoItem.getTodoTitle());
                }
            }

            log.info("待办事项保存完成，共保存 {} 条", savedCount);
        } catch (Exception e) {
            log.error("保存待办事项时发生异常", e);
            // 不抛出异常，避免影响主流程
        }
    }

    /**
     * 解析并保存SOP评分信息
     */
    private void saveSopInfo(
            JsonNode root,
            String parentId,
            String ownerName,
            String ownerPhone,
            String customerName,
            String customerPhone) {
        try {
            JsonNode comprehensiveScoreNode = root.get("comprehensive_score");
            if (comprehensiveScoreNode == null) {
                log.info("未找到SOP评分信息，跳过保存");
                return;
            }

            AudioTextAnalysisSop sop = new AudioTextAnalysisSop();
            sop.setId(UUID.randomUUID().toString());
            sop.setParentId(parentId);
            sop.setOwnerName(ownerName);
            sop.setOwnerPhone(ownerPhone);
            sop.setCustomerName(customerName);
            sop.setCustomerPhone(customerPhone);
            sop.setIndustryType("furniture");

            // 解析SOP评分字段
            sop.setComprehensiveScore(intValue(root, "comprehensive_score"));
            sop.setScoreSummaryReason(textValue(root, "score_summary_reason"));
            sop.setGreetingIceBreaking(intValue(root, "greeting_ice_breaking"));
            sop.setBrandIntroduction(intValue(root, "brand_introduction"));
            sop.setGoldenThreeQuestions(intValue(root, "golden_three_questions"));
            sop.setNeedsGuidance(intValue(root, "needs_guidance"));
            sop.setServiceProgression(intValue(root, "service_progression"));
            sop.setReassuranceHandbook(intValue(root, "reassurance_handbook"));
            sop.setThreeLevelPricing(intValue(root, "three_level_pricing"));
            sop.setObjectionHandling(intValue(root, "objection_handling"));
            sop.setActivityImplantation(intValue(root, "activity_implantation"));
            sop.setClosingCooperation(intValue(root, "closing_cooperation"));
            sop.setProactiveWechatAdd(intValue(root, "proactive_wechat_add"));
            sop.setPoliteFarewell(intValue(root, "polite_farewell"));

            // 设置时间
            LocalDateTime now = LocalDateTime.now();
            sop.setCreatedAt(now);
            sop.setUpdatedAt(now);

            // 检查是否已存在相同parentId的记录，如果存在则更新，否则新增
            LambdaQueryWrapper<AudioTextAnalysisSop> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(AudioTextAnalysisSop::getParentId, parentId);
            AudioTextAnalysisSop existingSop = sopService.getOne(wrapper);
            if (existingSop != null) {
                sop.setId(existingSop.getId());
                boolean success = sopService.updateById(sop);
                if (success) {
                    log.info("更新SOP评分信息成功，ID: {}, parentId: {}", sop.getId(), parentId);
                } else {
                    log.warn("更新SOP评分信息失败，parentId: {}", parentId);
                }
            } else {
                boolean success = sopService.save(sop);
                if (success) {
                    log.info("保存SOP评分信息成功，ID: {}, parentId: {}", sop.getId(), parentId);
                } else {
                    log.warn("保存SOP评分信息失败，parentId: {}", parentId);
                }
            }
        } catch (Exception e) {
            log.error("保存SOP评分信息时发生异常", e);
            // 不抛出异常，避免影响主流程
        }
    }

    /**
     * 从JSON节点中获取整数值
     */
    private Integer intValue(JsonNode root, String field) {
        JsonNode node = root.get(field);
        if (node == null || node.isNull()) {
            return null;
        }
        if (node.isInt()) {
            return node.asInt();
        } else if (node.isTextual()) {
            try {
                String text = node.asText();
                if (text == null || text.trim().isEmpty()) {
                    return null;
                }
                return Integer.parseInt(text.trim());
            } catch (NumberFormatException e) {
                log.warn("无法将字段 {} 的值 {} 转换为整数", field, node.asText());
                return null;
            }
        }
        return null;
    }
}


