package com.sonic.cow.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.byteplus.ark.runtime.model.completion.chat.ChatMessage;
import com.byteplus.ark.runtime.model.completion.chat.ChatMessageRole;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.sonic.cow.client.ResponseChatClient;
import com.sonic.cow.client.VoiceChatClient;
import com.sonic.cow.client.input.voicechat.domain.AgentConfig;
import com.sonic.cow.client.input.voicechat.domain.Audio;
import com.sonic.cow.client.input.voicechat.domain.UserPrompt;
import com.sonic.cow.client.request.ContentRequest;
import com.sonic.cow.client.request.InputRequest;
import com.sonic.cow.client.response.ResponseChatResponse;
import com.sonic.cow.client.rtc.callback.conversation.Conv;
import com.sonic.cow.client.rtc.callback.conversation.StageCode;
import com.sonic.cow.client.rtc.callback.rts.Data;
import com.sonic.cow.client.rtc.callback.rts.Subv;
import com.sonic.cow.domain.entity.PromptConfig;
import com.sonic.cow.domain.entity.VoiceChatRecord;
import com.sonic.cow.domain.input.GenerateRtcTokenInput;
import com.sonic.cow.domain.input.VoiceChatOptInput;
import com.sonic.cow.domain.output.GenerateRtcTokenOutput;
import com.sonic.cow.domain.output.VoiceChatOptOutput;
import com.sonic.cow.enums.*;
import com.sonic.cow.event.inner.payload.EmotionScorePayload;
import com.sonic.cow.event.outer.payload.UserDeductionStatPayload;
import com.sonic.cow.service.*;
import com.sonic.cow.utils.Constant;
import com.sonic.cow.utils.RedisKeyUtils;
import com.sonic.cow.utils.TimerUtils;
import com.sonic.frog.lib.client.AiChatInfoClient;
import com.sonic.frog.lib.client.AiClient;
import com.sonic.frog.lib.enums.HeartbeatLevelEnum;
import com.sonic.frog.lib.output.AiChatInfoOutput;
import com.sonic.frog.lib.output.AiInfoApiOutput;
import com.sonic.pigeon.lib.client.ImMessageClient;
import com.sonic.pigeon.lib.enums.ImMessageTypeEnum;
import com.sonic.pigeon.lib.input.SendAiCustomerMessageInput;
import com.sonic.pigeon.lib.output.HistoryMessageOutput;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @description: 语音通话实现类
 * @author: mzc
 * @date: 2025-07-30 11:24
 **/
@Slf4j
@Service
public class VoiceChatServiceImpl implements VoiceChatService {
    @Autowired
    private VoiceChatClient voiceChatClient;
    @Autowired
    private CommonSendMqService commonSendMqService;
    @Autowired
    private VoiceChatRecordService voiceChatRecordService;
    @Autowired
    private AiClient aiClient;
    @Autowired
    private InputRequestBuildService inputRequestBuildService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private AiChatInfoClient aiChatInfoClient;
    @Autowired
    private VoiceService voiceService;
    @Autowired
    private ContextSessionCacheService contextSessionCacheService;
    @Autowired
    private ResponseChatClient responseChatClient;
    @Autowired
    private PromptConfigService promptConfigService;
    @Autowired
    private RedisKeyUtils redisKeyUtils;
    @Autowired
    private ImMessageClient imMessageClient;


    @Override
    public GenerateRtcTokenOutput generateRtcToken(Long currentUserId, GenerateRtcTokenInput input) {
        return GenerateRtcTokenOutput.builder()
                .token(voiceChatClient.generateRtcToken(input.getRoomId(), currentUserId.toString()))
                .build();
    }

    @Override
    public VoiceChatOptOutput voiceChatOpt(Long currentUserId, VoiceChatOptInput input) {
        VoiceChatOptTypeEnum optType = input.getOptType();
        switch (optType) {
            case START:
                startVoiceChat(currentUserId, input);
                break;
            case INTERRUPT:
                interruptVoiceChat(currentUserId, input);
                break;
            case STOP:
                stopVoiceChat(currentUserId, input);
                break;
            case CANCEL:
                cancelVoiceChat(currentUserId, input);
                break;
        }
        return null;
    }

    /**
     * 开始语音通话
     *
     * @param currentUserId
     * @param input
     */
    private void startVoiceChat(Long currentUserId, VoiceChatOptInput input) {
        //调用开启语音通话,先关闭用户之前的语音通话记录
        stopVoiceChat(currentUserId);
        //TODO 步骤1：检测余额是否不足
        long startTime = TimerUtils.start();
        //检测余额是否不足
        boolean balanceIsInsufficient = voiceService.checkBalanceIsInsufficient(currentUserId, Constant.VOICE_CALL_DEDUCTION_AMOUNT);
        if (balanceIsInsufficient) {
            //余额不足，发送MQ，余额不足处理
            commonSendMqService.userBalanceInsufficientCheckoutMq(currentUserId);
            ToastResultCode.USER_BALANCE_INSUFFICIENT.check(true);
        }
        TimerUtils.logCost(startTime, "步骤1：检测余额是否不足");

        //TODO 步骤2: 获取ai基础信息
        startTime = TimerUtils.start();
        //真人用户id,ai用户id
        String targetUserId = currentUserId.toString();
        String aiUserId = targetUserId + "@" + input.getAiId();
        Long aiId = input.getAiId();
        //通过aiId获取对应ai的配置
        AiInfoApiOutput aiInfo = aiClient.getAiInfo(aiId);
        ToastResultCode.AI_NOT_EXIST.check(aiInfo == null);
        TimerUtils.logCost(startTime, "步骤2: 获取ai基础信息");

        //TODO 步骤3: 获取当前用户与ai的信息
        startTime = TimerUtils.start();
        //检测心动等级达到L4级才开启语音通话
        AiChatInfoOutput aiChatInfoOutput = aiChatInfoClient.getAiChatInfo(currentUserId, aiId);
        List<HeartbeatLevelEnum> unlockHearbeatLevelList = aiChatInfoOutput.getUnlockHearbeatLevelList();
        ToastResultCode.SYS_PERMISSION_DENIED.check(CollectionUtils.isEmpty(unlockHearbeatLevelList));
        ToastResultCode.SYS_PERMISSION_DENIED.check(!unlockHearbeatLevelList.contains(HeartbeatLevelEnum.LEVEL_4));
        //语音类型，语速，音高
        Audio ttsAudio = Audio.builder()
                .voice_type(StringUtils.isNotEmpty(aiInfo.getVoiceType()) ? aiInfo.getVoiceType() : "zh_female_cancan_mars_bigtts")
                //TODO 临时使用
//                .voice_type("zh_female_cancan_mars_bigtts")
                .speech_rate(StringUtils.isNotEmpty(aiInfo.getDialogueSpeechRate()) ? Integer.valueOf(aiInfo.getDialogueSpeechRate()) : 0)
                .pitch_rate(StringUtils.isNotEmpty(aiInfo.getDialoguePitch()) ? Integer.valueOf(aiInfo.getDialoguePitch()) : 0)
                .build();
        TimerUtils.logCost(startTime, "步骤3: 获取当前用户与ai的信息");

        //TODO 步骤4：获取语音通话系统提示词
        startTime = TimerUtils.start();
        AiInfoApiOutput aiUserInfo = aiClient.getAiInfo(input.getAiId());
        //系统提示
        String systemPrompt = inputRequestBuildService.buildVoiceChatSystemPromptContent(currentUserId, aiId, aiUserInfo);
        log.info("startVoiceChat systemPrompt:{}", systemPrompt);
        TimerUtils.logCost(startTime, "步骤4：获取语音通话系统提示词");

        //TODO 步骤5：获取用户与ai聊天历史记录
        startTime = TimerUtils.start();
        //用户与ai聊天历史记录
        List<HistoryMessageOutput> historyMessageList = inputRequestBuildService.getHistoryMessageList(currentUserId, aiId, 50);
        //构建语音通话聊天历史
        List<UserPrompt> userPromptList = inputRequestBuildService.buildVoiceChatUserPrompts(currentUserId, aiId, historyMessageList);
        log.info("startVoiceChat userPromptList:{}", JSON.toJSONString(userPromptList));
        TimerUtils.logCost(startTime, "步骤5：获取用户与ai聊天历史记录");

        //TODO 步骤6：根据聊天上下文生成开启语音对话开场白
        startTime = TimerUtils.start();
        //根据聊天上下文生成开启语音对话开场白
        String dialoguePrologue = genStartVoiceChatDialoguePrologue(currentUserId, aiId, systemPrompt, historyMessageList);
        TimerUtils.logCost(startTime, "步骤6：根据聊天上下文生成开启语音对话开场白");

        //TODO 步骤7：调字节接口开启语音通话
        startTime = TimerUtils.start();
        log.info("startVoiceChat dialoguePrologue:{}", dialoguePrologue);
        String welcomeMessage = StringUtils.isNotEmpty(dialoguePrologue) ? dialoguePrologue : aiInfo.getDialoguePrologue();
        log.info("startVoiceChat welcomeMessage:{}", welcomeMessage);
        AgentConfig agentConfig = AgentConfig.builder()
                .TargetUserID(new String[]{targetUserId})
                .UserId(aiUserId)
                .WelcomeMessage(welcomeMessage)
                .EnableConversationStateCallback(true)
                .ServerMessageURLForRTS("https://test-cow.crushlevel.ai/web/voice-chat/conversation-state-callback")
                .ServerMessageSignatureForRTS("123456")
                .build();
        //调用启动聊天通话
        voiceChatClient.startVoiceChat(input.getRoomId(), input.getTaskId(), ttsAudio, systemPrompt, userPromptList, agentConfig);
        TimerUtils.logCost(startTime, "步骤7：调字节接口开启语音通话");

        //TODO 步骤8：保存语音通话记录
        startTime = TimerUtils.start();
        //保存聊天通话记录
        Long voiceCallId = voiceChatRecordService.add(currentUserId, aiId, input.getRoomId(), input.getTaskId(), input.getEndpoint());
        //设置语音通话双方与房间关联的任务id,RTC回调的时候用到 STOP时删除,缓存1天，避免用户在1天之后，没有结束通话，导致无法删除
        stringRedisTemplate.opsForValue().set(redisKeyUtils.voiceChatTaskIdKey(targetUserId), input.getTaskId(), 1, TimeUnit.DAYS);
        stringRedisTemplate.opsForValue().set(redisKeyUtils.voiceChatTaskIdKey(aiUserId), input.getTaskId(), 1, TimeUnit.DAYS);
        //发送到MQ计算费用
        commonSendMqService.voiceCallDeductionSendMq(currentUserId, aiId, voiceCallId);
        TimerUtils.logCost(startTime, "步骤8：保存语音通话记录");
    }

    /**
     * 获取启动语音通话的对话开场白
     *
     * @param fromUserId
     * @param toUserId
     * @return
     */
    private String genStartVoiceChatDialoguePrologue(Long fromUserId, Long toUserId, String systemPrompt, List<HistoryMessageOutput> historyMessageList) {
        //获取模板
        PromptConfig systemPromptConfig = promptConfigService.getPromptTemplate(PromptTypeEnum.START_VOICE_CHAT_DIALOGUE_PROLOGUE.name());
        //获取上下文缓存 这个缓存一定是存在的
        String responseId = contextSessionCacheService.getByRedis(fromUserId, toUserId);
        List<InputRequest> inputRequests = Lists.newArrayList();
        if (StringUtils.isEmpty(responseId)) {
            //获取最近10条聊天历史
            List<HistoryMessageOutput> last10HistoryMessageList = getLast10HistoryMessageList(historyMessageList);
            //调用公共方法，组装入参数据
            inputRequestBuildService.buildStartVoiceChatDialoguePrologue(fromUserId, toUserId, inputRequests, systemPrompt, last10HistoryMessageList);
            //构造系统提示词，并放到第一个位置
            InputRequest systemMessage = InputRequest.builder()
                    .role(ChatMessageRole.SYSTEM)
                    .content(Lists.newArrayList(ContentRequest.builder().type("input_text").text(systemPrompt).build())).build();
            inputRequests.add(0, systemMessage);
        }
        //构造用户提示词
        inputRequests.add(InputRequest.builder()
                .role(ChatMessageRole.USER)
                .content(Lists.newArrayList(ContentRequest.builder().type("input_text").text(systemPromptConfig.getPromptTemplate()).build())).build());
        //执行聊天 不将添加缓存
        //TODO 步骤6-1：模型生成开场白
        long startTime = TimerUtils.start();
        ResponseChatResponse chatResponse = responseChatClient.responseStructuredChat(responseId, null, inputRequests, null, false);
        TimerUtils.logCost(startTime, "步骤6-1 模型生成开场白");

        log.info("===> genStartVoiceChatDialoguePrologue chatResponse : {}", chatResponse);
        if (chatResponse != null) {
            if (chatResponse.getMessage() != null) {
                String message = chatResponse.getMessage().replaceAll("（.*?）", "");
                message = message.replaceAll("\\(.*?\\)", "");
                return message;
            }
        }
        return null;
    }

    /**
     * 获取最近10条聊天历史
     *
     * @param historyMessageList
     * @return
     */
    private List<HistoryMessageOutput> getLast10HistoryMessageList(List<HistoryMessageOutput> historyMessageList) {
        // 1. 处理空列表（避免空指针）
        if (historyMessageList == null || historyMessageList.isEmpty()) {
            return new ArrayList<>(); // 或返回 Collections.emptyList()（不可修改）
        }

        // 2. 计算起始索引：总长度 - 10，若结果 < 0 则取 0（不足 10 条）
        int totalSize = historyMessageList.size();
        int startIndex = Math.max(0, totalSize - 10);

        // 3. 截取后 10 条（subList 是原列表的视图，若需独立列表可转成新 ArrayList）
        return new ArrayList<>(historyMessageList.subList(startIndex, totalSize));
    }

    /**
     * 打断语音通话
     *
     * @param currentUserId
     * @param input
     */
    private void interruptVoiceChat(Long currentUserId, VoiceChatOptInput input) {
        //打断
        voiceChatClient.updateVoiceChat(input.getRoomId(), input.getTaskId());
        //更新通话状态为打断
        voiceChatRecordService.updateStatus(input.getRoomId(), input.getTaskId(), VoiceChatStatusEnum.INTERRUPT);
    }

    /**
     * 结束语音通话
     *
     * @param currentUserId
     * @param input
     */
    private void stopVoiceChat(Long currentUserId, VoiceChatOptInput input) {
        //真人用户id,ai用户id
        String targetUserId = currentUserId.toString();
        String aiUserId = targetUserId + "@" + input.getAiId();
        //查询房间是否已经结束，如果已经结束则快速返回
        VoiceChatRecord voiceChatRecord = voiceChatRecordService.getByRoomIdAndTaskId(input.getRoomId(), input.getTaskId());
        if (voiceChatRecord == null || voiceChatRecord.getStatus() == 3) {
            return;
        }
        //结束聊天通话
        voiceChatClient.stopVoiceChat(input.getRoomId(), input.getTaskId());
        //记录通话时长
        voiceChatRecordService.updateDuration(input.getRoomId(), input.getTaskId(), input.getDuration());
        //获取语音通话最后对话轮次
        String cacheRoundId = stringRedisTemplate.opsForValue().get(redisKeyUtils.voiceChatRoundIdKey(input.getTaskId()));
        if (cacheRoundId != null) {
            //有对话，获取最后一次对话内容，发送MQ进行情绪打分，并进行结束处理
            getRoundDataAndScore(input.getTaskId(), cacheRoundId, true, input.getDuration());
        } else {
            //没有对话，结束处理
            voiceCallEndHandler(currentUserId, input.getAiId(), null, input.getDuration());
        }
        //删除设置语音通话双方关联的任务id
        stringRedisTemplate.delete(redisKeyUtils.voiceChatTaskIdKey(targetUserId));
        stringRedisTemplate.delete(redisKeyUtils.voiceChatTaskIdKey(aiUserId));
        //删除缓存 对话轮次数据，缓存数据
        stringRedisTemplate.delete(redisKeyUtils.voiceChatRoundIdKey(input.getTaskId()));
        stringRedisTemplate.delete(redisKeyUtils.voiceChatRoundDataKey(input.getTaskId()));
        //发送到用户预扣费统计队列
        userDeductionStatSendMq(input.getRoomId(), input.getTaskId());
    }

    /**
     * 取消语音通话
     *
     * @param currentUserId
     * @param input
     */
    private void cancelVoiceChat(Long currentUserId, VoiceChatOptInput input) {
        voiceChatClient.stopVoiceChat(input.getRoomId(), input.getTaskId());
    }


    /**
     * 语音通话结束，发送用户预扣费统计队列，捐款，生成流水
     *
     * @param roomId
     * @param taskId
     */
    private void userDeductionStatSendMq(String roomId, String taskId) {
        VoiceChatRecord voiceChatRecord = voiceChatRecordService.getByRoomIdAndTaskId(roomId, taskId);
        if (voiceChatRecord == null) {
            return;
        }
        HashMap<Object, Object> extraMap = Maps.newHashMap();
        extraMap.put("voiceCallId", voiceChatRecord.getId());
        extraMap.put("duration", voiceChatRecord.getDuration());
        extraMap.put("status", voiceChatRecord.getStatus());
        commonSendMqService.userDeductionStatSendMq(UserDeductionStatPayload.builder()
                .userId(voiceChatRecord.getUserId())
                .aiId(voiceChatRecord.getAiId())
                .deductionType(DeductionTypeEnum.VOICE_CALL)
                .bizTime(LocalDateTime.now())
                .extra(JSON.toJSONString(extraMap))
                .build());
    }

    @Override
    public void webhook(HttpServletRequest request, HttpServletResponse response) {
        voiceChatClient.webhook(request, response);
    }

    @Deprecated
    @Override
    public void conversationStateCallback(HttpServletRequest request, HttpServletResponse response) {
        Conv conv = voiceChatClient.conversationStateCallback(request, response);
        log.info("conversationStateCallback conv:{}", conv);
        if (conv != null && conv.getStage() != null) {
            //智能体完成说话，发送MQ计算心动等级
            if (StageCode.ANSWER_FINISH.getCode() == conv.getStage().getCode()) {
                String taskId = conv.getTaskId();
            }
        }
    }

    @Override
    public void rtsCallback(HttpServletRequest request, HttpServletResponse response) {
        Subv subv = voiceChatClient.rtsCallback(request, response);
        if (subv == null) {
            return;
        }
        List<Data> dataList = subv.getData();
        log.info("rtsCallback dataList:{}", dataList);
        Data data = dataList.get(0);
        if (data != null && data.getRoundId() != null && data.getRoundId() > 0) {
            //开启语音通话时，给值
            String taskId = stringRedisTemplate.opsForValue().get(redisKeyUtils.voiceChatTaskIdKey(data.getUserId()));
            log.info("rtsCallback taskId:{}", taskId);
            if (taskId == null) {
                return;
            }
            //当前轮次与cache轮次不一样，说明上一轮次对方完，获取对话上一轮次语音内容并打分
            String cacheRoundId = stringRedisTemplate.opsForValue().get(redisKeyUtils.voiceChatRoundIdKey(taskId));
            if (cacheRoundId != null && !cacheRoundId.equals(data.getRoundId().toString())) {
                getRoundDataAndScore(taskId, cacheRoundId, false, null);
            }
            //将每次对话内容保存到redis Hash中，key为roundId，value为每次用户对话内容 结构为 Map<Integer,Map<String,List<Content>>
            cacheRoundData(taskId, data);
            //设置对话轮次
            stringRedisTemplate.opsForValue().set(redisKeyUtils.voiceChatRoundIdKey(taskId), data.getRoundId().toString());
        }
    }

    /**
     * 获取对话轮次语音内容
     *
     * @param taskId
     * @param cacheRoundId
     */
    private void getRoundDataAndScore(String taskId, String cacheRoundId, boolean isEnd, Long duration) {
        //对话轮次不一致的话，拼成cacheRoundId对应的用户与AI的完整内容 注：最后一次需要在停止语音通话时，将cacheRoundId对应的用户与AI的完整内容拼成完整内容，并保存到数据库中
        String cacheRoundDataStr = (String) stringRedisTemplate.opsForHash().get(redisKeyUtils.voiceChatRoundDataKey(taskId), cacheRoundId.toString());
        if (StringUtils.isNotEmpty(cacheRoundDataStr)) {
            Map<String, List<Data>> userDataMap = JSON.parseObject(cacheRoundDataStr, new TypeReference<Map<String, List<Data>>>() {
            });

            List<ChatMessage> messageList = Lists.newArrayList();
            for (Map.Entry<String, List<Data>> userEntry : userDataMap.entrySet()) {
                //用户id
                String userId = userEntry.getKey();
                //用户对应的语音内容
                List<Data> dataList = userEntry.getValue();
                //以升序排序
                dataList.sort(Comparator.comparingInt(Data::getSequence));
                //获取用户对应轮次的完整语音内容
                String content = dataList.stream().map(Data::getText).collect(Collectors.joining());
                //不为空的时候才拼成消息记录
                if (StringUtils.isNotEmpty(content)) {
                    messageList.add(ChatMessage.builder()
                            .role(userId.contains("@") ? ChatMessageRole.ASSISTANT : ChatMessageRole.USER)
                            .content(content)
                            .build());
                }
            }
            //打印拼出的轮次的完整语音内容
            log.info("getRoundDataAndScore round:{},messageList: {}", cacheRoundId, JSON.toJSONString(messageList));
            //发送MQ,调用模型让模型计算情绪得分 消息数成对
            if (CollectionUtils.isNotEmpty(messageList)) {
                //倒序一下，不然ai说的话在前面，用户说在后面，
                Collections.reverse(messageList);
                //发送MQ，语音通话情绪得分
                //taskId 实际由用户id-aiId-时间戮组成
                String[] split = taskId.split("-");
                Long voiceUserId = Long.valueOf(split[0]);
                Long voiceAiId = Long.valueOf(split[1]);
                commonSendMqService.emotionScoreSendMq(EmotionScorePayload.builder()
                        .chatMessageList(messageList)
                        .userId(voiceUserId)
                        .aiId(voiceAiId)
                        .isEnd(isEnd)
                        .duration(duration).build());
            }
        }
    }

    /**
     * 缓存对话轮次数据
     *
     * @param taskId
     * @param data
     */
    private void cacheRoundData(String taskId, Data data) {
        //获取对话轮次数据
        String voiceChatRoundDataKey = redisKeyUtils.voiceChatRoundDataKey(taskId);
        String roundDataStr = (String) stringRedisTemplate.opsForHash().get(voiceChatRoundDataKey, data.getRoundId().toString());
        //本对话轮次用户数据Map
        Map<String, List<Data>> userDataMap = new HashMap<>();
        if (StringUtils.isEmpty(roundDataStr)) {
            //没有，初始化
            userDataMap.put(data.getUserId(), Lists.newArrayList(data));
        } else {
            //存在，更新
            userDataMap = JSON.parseObject(roundDataStr, new TypeReference<Map<String, List<Data>>>() {
            });
            //该轮次对应用户有没有数据
            List<Data> userData = userDataMap.get(data.getUserId());
            if (CollectionUtils.isNotEmpty(userData)) {
                //有追加
                userData.add(data);
            } else {
                //没有，初始化
                userData = Lists.newArrayList(data);
            }
            userDataMap.put(data.getUserId(), userData);
        }
        //保存本轮次语音内容到缓存中
        stringRedisTemplate.opsForHash().put(voiceChatRoundDataKey, data.getRoundId().toString(), JSON.toJSONString(userDataMap));
        //设置对话轮次缓存有效期
        stringRedisTemplate.expire(voiceChatRoundDataKey, 1, TimeUnit.DAYS);
    }

    @Override
    public void voiceCallEndHandler(Long userId, Long aiId, List<InputRequest> voiceChatInputRequestList, Long duration) {
        //发送IM自定义消息
        Map<String, Object> extra = new HashMap<>();
        extra.put("type", ImMessageTypeEnum.CALL.name());
        if (CollectionUtils.isNotEmpty(voiceChatInputRequestList) && voiceChatInputRequestList.size() > 1) {
            extra.put("voiceChatInputRequestList", JSON.toJSONString(voiceChatInputRequestList));
        }
        extra.put("callType", "CALL_END");
        extra.put("duration", duration);
        SendAiCustomerMessageInput sendAiCustomerMessageInput = new SendAiCustomerMessageInput();
        sendAiCustomerMessageInput.setFromUserId(aiId);
        sendAiCustomerMessageInput.setToUserId(userId);
        sendAiCustomerMessageInput.setContent("语音通话");
        sendAiCustomerMessageInput.setAttachment(JSONObject.toJSONString(extra));
        imMessageClient.sendAiToUserCustomerMessage(sendAiCustomerMessageInput);
        //删除responseId缓存
        if (CollectionUtils.isNotEmpty(voiceChatInputRequestList)) {
            contextSessionCacheService.deleteRedis(userId, aiId);
        }
    }

    @Override
    public Boolean stopVoiceChat(Long userId) {
        //通过用户id获取taskId
        String taskId = stringRedisTemplate.opsForValue().get(redisKeyUtils.voiceChatTaskIdKey(userId.toString()));
        log.info("stopVoiceChat taskId:{}", taskId);
        //如果删除了，则直接返回
        if (taskId == null) {
            return true;
        }
        VoiceChatRecord voiceChatRecord = voiceChatRecordService.getByTaskId(taskId);
        log.info("stopVoiceChat voiceChatRecord:{}", JSON.toJSONString(voiceChatRecord));
        //已经结束，直接return
        if (voiceChatRecord.getStatus() == 3) {
           return true;
        }
        Long startTime = voiceChatRecord.getCreateTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
        Long duration = (System.currentTimeMillis() - startTime);
        //调用stopVoiceChat接口,停止语音通话
        VoiceChatOptInput voiceChatOptInput = VoiceChatOptInput.builder()
                .aiId(voiceChatRecord.getAiId())
                .roomId(voiceChatRecord.getRoomId())
                .optType(VoiceChatOptTypeEnum.STOP)
                .taskId(taskId)
                .duration(duration)
                .build();
        log.info("stopVoiceChat voiceChatOptInput:{} ", JSON.toJSONString(voiceChatOptInput));
        voiceChatOpt(voiceChatRecord.getUserId(), voiceChatOptInput);
        return true;
    }
}