package com.sonic.cow.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.byteplus.ark.runtime.model.completion.chat.ChatMessageRole;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.sonic.bear.lib.client.UserSearchClient;
import com.sonic.bear.lib.output.BaseUserInfoOutput;
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.domain.bo.AiChatPromptConfigBo;
import com.sonic.cow.domain.bo.ExtJsonBo;
import com.sonic.cow.domain.bo.ImMessageVoiceCallAttachBo;
import com.sonic.cow.domain.entity.PromptConfig;
import com.sonic.cow.enums.PromptTypeEnum;
import com.sonic.cow.enums.SexEnums;
import com.sonic.cow.event.outer.payload.AiChatPayload;
import com.sonic.cow.service.InputRequestBuildService;
import com.sonic.cow.service.PromptConfigService;
import com.sonic.cow.utils.BeanConvert;
import com.sonic.cow.utils.DateConvert;
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.output.AiChatInfoOutput;
import com.sonic.frog.lib.output.AiInfoApiOutput;
import com.sonic.pigeon.lib.bo.AttachBo;
import com.sonic.pigeon.lib.client.ImMessageClient;
import com.sonic.pigeon.lib.enums.ImMessageTypeEnum;
import com.sonic.pigeon.lib.enums.MessageTypeEnum;
import com.sonic.pigeon.lib.input.HistoryMessageInput;
import com.sonic.pigeon.lib.output.HistoryMessageOutput;
import freemarker.template.Configuration;
import freemarker.template.Template;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.StringWriter;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class InputRequestBuildServiceImpl implements InputRequestBuildService {

    @Autowired
    private ImMessageClient imMessageClient;
    @Autowired
    private AiClient aiClient;
    @Autowired
    private UserSearchClient userSearchClient;
    @Autowired
    private PromptConfigService promptConfigService;
    @Autowired
    private AiChatInfoClient aiChatInfoClient;

    private static final Configuration configuration = new Configuration(Configuration.VERSION_2_3_29);

    @Override
    public void buildV2(AiChatPayload payload, List<InputRequest> inputRequests, Integer historyMessageLimit, Boolean isText) {
        if (inputRequests == null) {
            return;
        }
        HistoryMessageInput input = new HistoryMessageInput();
        input.setFromUserId(payload.getFromUserId());
        input.setToUserId(payload.getToUserId());
        input.setLimit(historyMessageLimit);
        input.setDescending(false);
        //rpc获取im的历史消息
        List<HistoryMessageOutput> historyMessageOutputs = imMessageClient.getHistoryMessage(input);
        log.info("buildV2 historyMessageOutputs:{}", JSON.toJSONString(historyMessageOutputs));
        //构建inputRequestList
        buildInputRequestList(payload.getFromUserId(), inputRequests, historyMessageOutputs);

        //获取AI基础信息
        AiInfoApiOutput aiUserInfo = aiClient.getAiInfo(payload.getToUserId());
        //构造系统提示词，并放到第一个位置
        InputRequest systemMessage;
        if (isText) {
            systemMessage = InputRequest.builder().role(ChatMessageRole.SYSTEM).content(Lists.newArrayList(ContentRequest.builder().type("input_text").text(buildSystemPromptContent(payload.getFromUserId(), payload.getToUserId(), aiUserInfo)).build())).build();
        } else {
            systemMessage = InputRequest.builder().role(ChatMessageRole.SYSTEM).content(Lists.newArrayList(ContentRequest.builder().type("input_text").text(buildVoiceChatSystemPromptContent(payload.getFromUserId(), payload.getToUserId(), aiUserInfo)).build())).build();
        }
        inputRequests.add(0, systemMessage);
        //文本聊天 且 当历史消息条数不足的时候才构造开场白，也就是说小于20条的时候才构造，其他条件下不进行构造
        if (inputRequests.size() < 20) {
            //构造开场白
            InputRequest dialoguePrologueMessage = InputRequest.builder().role(ChatMessageRole.ASSISTANT).content(Lists.newArrayList(ContentRequest.builder().type("input_text").text(aiUserInfo.getDialoguePrologue()).build())).build();
            inputRequests.add(1, dialoguePrologueMessage);
        }
    }

    @Override
    public void buildStartVoiceChatDialoguePrologue(Long fromUserId, Long toUserId, List<InputRequest> inputRequests, String systemPrompt, List<HistoryMessageOutput> historyMessageList) {
        buildInputRequestList(fromUserId, inputRequests, historyMessageList);
    }

    /**
     * 构建输入请求列表
     *
     * @param fromUserId
     * @param inputRequests
     * @param historyMessageList
     */
    private static void buildInputRequestList(Long fromUserId, List<InputRequest> inputRequests, List<HistoryMessageOutput> historyMessageList) {
        for (HistoryMessageOutput historyMessageOutput : historyMessageList) {
            //自定义消息，除了送礼物，图片消息, 语音通话内容，其他的直接break;
            if (MessageTypeEnum.CUSTOM == historyMessageOutput.getMessageType()) {
                AttachBo attachBo = historyMessageOutput.getAttachment() == null ? null : JSONObject.parseObject(historyMessageOutput.getAttachment().toString(), AttachBo.class);
                log.info("buildV2 attachBo:{}", attachBo);
                if (attachBo != null && !ImMessageTypeEnum.IMAGE.name().equalsIgnoreCase(attachBo.getType())
                        && !ImMessageTypeEnum.IM_SEND_GIFT.name().equalsIgnoreCase(attachBo.getType()) && !ImMessageTypeEnum.CALL.name().equalsIgnoreCase(attachBo.getType())) {
                    continue;
                }
                //语音通话内容，特殊处理,把语音通话内容组装成inputRequest
                if (attachBo != null && ImMessageTypeEnum.CALL.name().equalsIgnoreCase(attachBo.getType())) {
                    ImMessageVoiceCallAttachBo imMessageVoiceCallAttachBo = JSONObject.parseObject(historyMessageOutput.getAttachment().toString(), ImMessageVoiceCallAttachBo.class);
                    log.info("buildV2 imMessageVoiceCallAttachBo:{}", attachBo);
                    if (imMessageVoiceCallAttachBo != null && StringUtils.isNotBlank(imMessageVoiceCallAttachBo.getVoiceChatInputRequestList())) {
                        List<InputRequest> inputRequestList = JSONObject.parseArray(imMessageVoiceCallAttachBo.getVoiceChatInputRequestList(), InputRequest.class);
                        inputRequests.addAll(inputRequestList);
                    }
                    continue;
                }
            }
            //发送人是用户，且偶数条数时组装
            if (fromUserId.equals(historyMessageOutput.getFromUserId()) && inputRequests.size() % 2 == 0) {
                //组装用户消息
                List<ContentRequest> contentRequestList = Lists.newArrayList();
                contentRequestList.add(ContentRequest.builder().type("input_text").text(historyMessageOutput.getContent()).build());
                InputRequest message = InputRequest.builder().role(ChatMessageRole.USER).content(contentRequestList).build();
                inputRequests.add(message);
            } else if (!fromUserId.equals(historyMessageOutput.getFromUserId()) && inputRequests.size() % 2 == 1) {
                //奇数条数时组装
                //组装AI消息
                InputRequest message = InputRequest.builder().role(ChatMessageRole.ASSISTANT).content(Lists.newArrayList(ContentRequest.builder().type("input_text").text(historyMessageOutput.getContent()).build())).build();
                inputRequests.add(message);
            }
        }
        //组装出来的数据必须是偶数条，否则需要移除最后一条数据
        if (inputRequests.size() % 2 != 0) {
            inputRequests.remove(inputRequests.size() - 1);
        }
    }


    /**
     * 获取用户与ai的历史消息
     *
     * @param fromUserId
     * @param toUserId
     * @return
     */
    public List<HistoryMessageOutput> getHistoryMessageList(Long fromUserId, Long toUserId, Integer limitNum) {
        List<HistoryMessageOutput> historyMessageOutputs = Lists.newArrayList();
        try {
            HistoryMessageInput input = new HistoryMessageInput();
            input.setFromUserId(fromUserId);
            input.setToUserId(toUserId);
            input.setLimit(limitNum);
            input.setDescending(false);
            //rpc获取im的历史消息
            return imMessageClient.getHistoryMessage(input);
        } catch (Exception e) {
            log.error("getHistoryMessage error:{}", e.getMessage());
        }
        return historyMessageOutputs;
    }

    @Override
    public List<UserPrompt> buildVoiceChatUserPrompts(Long fromUserId, Long toUserId, List<HistoryMessageOutput> historyMessageList) {
        List<UserPrompt> userPromptList = Lists.newArrayList();
        for (HistoryMessageOutput historyMessageOutput : historyMessageList) {
            //自定义消息，除了送礼物，图片消息, 语音通话内容，其他的直接break;
            if (MessageTypeEnum.CUSTOM == historyMessageOutput.getMessageType()) {
                AttachBo attachBo = historyMessageOutput.getAttachment() == null ? null : JSONObject.parseObject(historyMessageOutput.getAttachment().toString(), AttachBo.class);
                log.info("buildV2 attachBo:{}", attachBo);
                if (attachBo != null && !ImMessageTypeEnum.IMAGE.name().equalsIgnoreCase(attachBo.getType())
                        && !ImMessageTypeEnum.IM_SEND_GIFT.name().equalsIgnoreCase(attachBo.getType()) && !ImMessageTypeEnum.CALL.name().equalsIgnoreCase(attachBo.getType())) {
                    continue;
                }
                //语音通话内容，特殊处理,把语音通话内容组装成inputRequest
                if (attachBo != null && ImMessageTypeEnum.CALL.name().equalsIgnoreCase(attachBo.getType())) {
                    ImMessageVoiceCallAttachBo imMessageVoiceCallAttachBo = JSONObject.parseObject(historyMessageOutput.getAttachment().toString(), ImMessageVoiceCallAttachBo.class);
                    log.info("buildV2 imMessageVoiceCallAttachBo:{}", attachBo);
                    if (imMessageVoiceCallAttachBo != null && StringUtils.isNotBlank(imMessageVoiceCallAttachBo.getVoiceChatInputRequestList())) {
                        List<InputRequest> inputRequestList = JSONObject.parseArray(imMessageVoiceCallAttachBo.getVoiceChatInputRequestList(), InputRequest.class);
                        for (InputRequest inputRequest : inputRequestList) {
                            ChatMessageRole role = inputRequest.getRole();
                            List<ContentRequest> contentRequestList = inputRequest.getContent();
                            if (CollectionUtils.isNotEmpty(contentRequestList)) {
                                String content = contentRequestList.get(0).getText() != null ? contentRequestList.get(0).getText() : "";
                                UserPrompt message = UserPrompt.builder().Role(role.value()).Content(content.replaceAll("（.*?）", "")).build();
                                userPromptList.add(message);
                            }
                        }
                    }
                    continue;
                }
            }
            String role = "assistant";
            //发送人是用户，且偶数条数时组装
            if (fromUserId.equals(historyMessageOutput.getFromUserId())) {
                role = "user";
            }
            if (StringUtils.isNotBlank(historyMessageOutput.getContent())) {
                UserPrompt message = UserPrompt.builder().Role(role).Content(historyMessageOutput.getContent().replaceAll("（.*?）", "")).build();
                userPromptList.add(message);
            }
        }
        return userPromptList;
    }


    /**
     * 构造系统提示词
     *
     * @param fromUserId
     * @param toUserId
     * @return
     */
    @Override
    public String buildSystemPromptContent(Long fromUserId, Long toUserId, AiInfoApiOutput aiUserInfo) {
        //数据库查询模板数据
        PromptConfig systemPromptConfig = promptConfigService.getPromptTemplate(PromptTypeEnum.CHAT_SYSTEM_PROMPT_TEMPLATE.name());
        return buildSystemPromptContent(fromUserId, toUserId, aiUserInfo, systemPromptConfig);
    }

    /**
     * 构造语音通话系统提示词
     *
     * @param fromUserId
     * @param toUserId
     * @return
     */
    public String buildVoiceChatSystemPromptContent(Long fromUserId, Long toUserId, AiInfoApiOutput aiUserInfo) {
        //数据库查询模板数据
        PromptConfig systemPromptConfig = promptConfigService.getPromptTemplate(PromptTypeEnum.VOICE_CHAT_SYSTEM_PROMPT_TEMPLATE.name());
        return buildSystemPromptContent(fromUserId, toUserId, aiUserInfo, systemPromptConfig);
    }


    /**
     * 构造系统提示词
     *
     * @param fromUserId
     * @param toUserId
     * @return
     */
    public String buildSystemPromptContent(Long fromUserId, Long toUserId, AiInfoApiOutput aiUserInfo, PromptConfig systemPromptConfig) {
        AiChatPromptConfigBo promptConfigBo = new AiChatPromptConfigBo();
        if(StringUtils.isNotEmpty(aiUserInfo.getUserProfileExtJson())) {
            ExtJsonBo extJsonBo = JSONObject.parseObject(aiUserInfo.getUserProfileExtJson(), ExtJsonBo.class);
            promptConfigBo = BeanConvert.copeBean(extJsonBo, AiChatPromptConfigBo.class);
        }
        //TODO 构造系统提示词 步骤1 获取ai基础信息
        long startTime = TimerUtils.start();
        promptConfigBo.setAiNickname(aiUserInfo.getNickname());
        promptConfigBo.setAiSex(SexEnums.getSex(aiUserInfo.getSex().toString()));
        promptConfigBo.setAiBirthday(DateConvert.localDateTime2String(aiUserInfo.getBirthday()));
        TimerUtils.logCost(startTime, "构造系统提示词 步骤1 获取ai基础信息");

        //TODO 构造系统提示词 步骤2 查询AI个性字典获取提示词
        startTime = TimerUtils.start();
        //查询AI个性字典获取提示词
        PromptConfig personalityTraitsPromptConfig = promptConfigService.getPromptTemplate(aiUserInfo.getCharacterCode() + "|" + aiUserInfo.getTagCode());
        promptConfigBo.setAiPersonalityTraits(personalityTraitsPromptConfig.getPromptTemplate());
        promptConfigBo.setAiProfile(aiUserInfo.getProfile());
        promptConfigBo.setAiDialogueStyle(StringUtils.isNotEmpty(aiUserInfo.getDialogueStyle()) ? aiUserInfo.getDialogueStyle() : "");
        TimerUtils.logCost(startTime, "构造系统提示词 步骤2 查询AI个性字典获取提示词");

        //TODO 构造系统提示词 步骤3 获取当前用户和AI的聊天基础设置
        startTime = TimerUtils.start();
        //获取当前用户和AI的聊天基础设置
        AiChatInfoOutput aiChatInfoOutput = aiChatInfoClient.getAiChatInfo(fromUserId, toUserId);
        TimerUtils.logCost(startTime, "构造系统提示词 步骤3 获取当前用户和AI的聊天基础设置");

        //TODO 构造系统提示词 步骤4 模板对象赋值
        startTime = TimerUtils.start();
        //处理认识天数+1处理
        aiChatInfoOutput.setDayCount(aiChatInfoOutput.getDayCount() == null ? 1 : aiChatInfoOutput.getDayCount() + 1);
        if (StringUtils.isEmpty(aiChatInfoOutput.getNickname()) && aiChatInfoOutput.getSex() == null && aiChatInfoOutput.getBirthday() == null) {
            //获取当前用户的基础信息
            BaseUserInfoOutput fromUserInfo = userSearchClient.baseUserInfo(fromUserId);
            promptConfigBo.setUserNickname(fromUserInfo.getNickname());
            promptConfigBo.setUserSex(SexEnums.getSex(fromUserInfo.getSex().toString()));
            promptConfigBo.setUserBirthday(DateConvert.localDateTime2String(fromUserInfo.getBirthday()));
        } else {
            promptConfigBo.setUserNickname(aiChatInfoOutput.getNickname());
            promptConfigBo.setUserSex(SexEnums.getSex(aiChatInfoOutput.getSex().toString()));
            promptConfigBo.setUserBirthday(DateConvert.localDateTime2String(aiChatInfoOutput.getBirthday()));
        }
        //为空的话就是未填写
        promptConfigBo.setUserProfile(StringUtils.isEmpty(aiChatInfoOutput.getWhoAmI()) ? "" : aiChatInfoOutput.getWhoAmI());
        //查询提示词
        PromptConfig relationshipPromptConfig = promptConfigService.getPromptTemplate(aiChatInfoOutput.getRelationStage());
        //用户关系阶段的描述词
        promptConfigBo.setRelationshipDesc(relationshipPromptConfig.getPromptTemplate());
        //用户关系阶段
        promptConfigBo.setRelationship(relationshipPromptConfig.getPromptName());
        //用户关系阶段中禁用的提示词
        promptConfigBo.setRelationshipBanned(relationshipPromptConfig.getPromptTemplate1());
        //用户已认识天数
        promptConfigBo.setDaysKnown(aiChatInfoOutput.getDayCount());
        //设置用户ID和aiId【用String接收处理科学计数法的问题】
        promptConfigBo.setUserId(fromUserId.toString());
        promptConfigBo.setAiId(toUserId.toString());
        promptConfigBo.setDialoguePrologue(aiUserInfo.getDialoguePrologue());

        //相识天数判断是否查询对话场景提示词
        if (aiChatInfoOutput.getDayCount() > 3) {
            //查询提示词
            PromptConfig dialogueScenarioPromptConfig = promptConfigService.getPromptTemplate(PromptTypeEnum.DIALOGUE_SCENARIO.name());
            //根据meet关系来判断对话场景
            promptConfigBo.setDialogueScenario(aiChatInfoOutput.getMeet() != null && aiChatInfoOutput.getMeet() ? dialogueScenarioPromptConfig.getPromptTemplate() : "");
        } else {
            promptConfigBo.setDialogueScenario("");
        }

        Map<String, Object> data = JSONObject.parseObject(JSONObject.toJSONString(promptConfigBo), Map.class);
        String template = StringUtils.isNotEmpty(promptConfigBo.getUserProfile()) ? systemPromptConfig.getPromptTemplate() : systemPromptConfig.getPromptTemplate1();
        TimerUtils.logCost(startTime, "构造系统提示词 步骤4 模板对象赋值");

        //执行真正的模板内容替换操作
        //TODO 构造系统提示词 步骤5 执行真正的模板内容替换操作
        startTime = TimerUtils.start();
        String content = processTemplate(template, data);
        TimerUtils.logCost(startTime, "构造系统提示词 步骤5 执行真正的模板内容替换操作");
        return content;
    }

    /**
     * 处理模板参数内容的替换
     *
     * @param templateString
     * @param data
     * @return
     * @throws Exception
     */
    public static String processTemplate(String templateString, Map<String, Object> data) {
        try {
            log.info("===> processTemplate data : {}, templateString : {}", data, templateString);
            Template template = new Template("template", templateString, configuration);
            StringWriter writer = new StringWriter();
            template.process(data, writer);
            return writer.toString();
        } catch (Exception e) {
            log.error("处理模板参数内容异常", e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public String buildVoiceCallEmotionScorePromptContent(String content) {
        PromptConfig systemPromptConfig = promptConfigService.getPromptTemplate(PromptTypeEnum.CHAT_SCORE_SYSTEM_PROMPT_TEMPLATE.name());
        Map<String, Object> data = Maps.newHashMap();
        data.put("content", content);
        String template = systemPromptConfig.getPromptTemplate();
        //执行真正的模板内容替换操作
        return processTemplate(template, data);
    }
}
