package cn.com.poc.expose.aggregate.impl;

import cn.com.poc.agent_application.aggregate.AgentApplicationService;
import cn.com.poc.agent_application.constant.AgentApplicationDialoguesRecordConstants;
import cn.com.poc.agent_application.constant.AgentApplicationGCConfigConstants;
import cn.com.poc.agent_application.entity.*;
import cn.com.poc.agent_application.query.AgentApplicationIndexPluginQueryCondition;
import cn.com.poc.agent_application.query.AgentApplicationIndexPluginQueryItem;
import cn.com.poc.agent_application.query.MemberCollectQueryCondition;
import cn.com.poc.agent_application.query.MemberCollectQueryItem;
import cn.com.poc.agent_application.service.*;
import cn.com.poc.agent_application.utils.AgentApplicationTools;
import cn.com.poc.common.constant.CommonConstant;
import cn.com.poc.common.constant.XLangConstant;
import cn.com.poc.common.service.RedisService;
import cn.com.poc.common.utils.BlContext;
import cn.com.poc.common.utils.JsonUtils;
import cn.com.poc.data_analyze.aggregate.DataAnalyzeReportService;
import cn.com.poc.data_analyze.constants.DataAnalyzeChannelEnum;
import cn.com.poc.equity.aggregate.MemberEquityService;
import cn.com.poc.equity.aggregate.PointDeductionRulesService;
import cn.com.poc.equity.constants.ModifyEventEnum;
import cn.com.poc.equity.domain.modifyEquityInfo.AgentUseModifyEventInfo;
import cn.com.poc.expose.aggregate.AgentApplicationExposeService;
import cn.com.poc.knowledge.aggregate.KnowledgeService;
import cn.com.poc.support.security.oauth.entity.UserBaseEntity;
import cn.com.poc.thirdparty.resource.demand.ai.constants.KnowledgeSearchTypeEnum;
import cn.com.poc.thirdparty.resource.demand.ai.constants.LLMRoleEnum;
import cn.com.poc.thirdparty.resource.demand.ai.entity.dialogue.Message;
import cn.com.poc.thirdparty.resource.demand.ai.entity.dialogue.MultiContent;
import cn.com.poc.thirdparty.resource.demand.ai.entity.dialogue.Tool;
import cn.com.poc.thirdparty.resource.demand.ai.entity.largemodel.LargeModelDemandResult;
import cn.com.poc.thirdparty.resource.demand.ai.entity.largemodel.LargeModelResponse;
import cn.com.poc.thirdparty.service.LLMService;
import cn.com.yict.framemax.core.exception.BusinessException;
import cn.com.yict.framemax.core.i18n.I18nMessageException;
import cn.com.yict.framemax.data.model.PagingInfo;
import com.fasterxml.jackson.core.type.TypeReference;
import com.github.houbb.opencc4j.util.ZhConverterUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class AgentApplicationExposeServiceImpl implements AgentApplicationExposeService {

    final private Logger logger = LoggerFactory.getLogger(AgentApplicationExposeService.class);

    /**
     * 推荐问Key
     */
    final private String AGENT_APPLICATION_RECOMMEND_QUESTIONS = "AGENT_APPLICATION_RECOMMEND_QUESTIONS:";

    /**
     * 用户获取推荐问题的上一次记录
     */
    final private String MEMBER_RECOMMEND_QUESTIONS_LAST = AGENT_APPLICATION_RECOMMEND_QUESTIONS + "MEMBER_LAST:";

    /**
     * 应用自动播放Key
     */
    final private String AGENT_APPLICATION_AUTO_PLAY = "AGENT_APPLICATION_AUTO_PLAY:";


    @Resource
    private BizMemberAgentApplicationCollectService bizMemberAgentApplicationCollectService;

    @Resource
    private KnowledgeService knowledgeService;

    @Resource
    private BizAgentApplicationPublishService bizAgentApplicationPublishService;

    @Resource
    private BizAgentApplicationDialoguesRecordService bizAgentApplicationDialoguesRecordService;

    @Resource
    private AgentApplicationService agentApplicationService;

    @Resource
    private BizAgentApplicationGcConfigService bizAgentApplicationGcConfigService;

    @Resource
    private LLMService llmService;

    @Resource
    private RedisService redisService;

    @Resource
    private PointDeductionRulesService pointDeductionRulesService;

    @Resource
    private MemberEquityService memberEquityService;

    @Resource
    private DataAnalyzeReportService dataAnalyzeReportService;

    @Resource
    private BizAgentApplicationApiProfileService bizAgentApplicationApiProfileService;

    @Resource
    private BizAgentApplicationIndexPluginService bizAgentApplicationIndexPluginService;


    @Override
    public void callAgentApplication(String agentId, String dialogsId, String input, List<String> fileUrls, String channel, String imageUrl, HttpServletResponse httpServletResponse) throws Exception {

        UserBaseEntity userBaseEntity = BlContext.getCurrentUserNotException();
        if (userBaseEntity == null) {
            throw new I18nMessageException("exception/user.not.login");
        }
        String reduceSn = "";
        try {
            BizAgentApplicationPublishEntity infoEntity = bizAgentApplicationPublishService.getByAgentId(agentId);
            if (infoEntity == null) {
                logger.warn("can not find agent application , agent_id:{}", agentId);
                throw new I18nMessageException("exception/app.not.found");
            }

            // 判断文件是否为空，如果不为空
//            AgentApplicationTools.checkDialogueContentIsEmpty(fileUrls);

            if (StringUtils.isBlank(dialogsId)) {
                // 用于针对只有单Agent应用分享使用的场景，dialogsId为空
                dialogsId = agentId + "-" + userBaseEntity.getUserId();
            }

            //获取知识库配置
            List<Integer> kdIdList = knowledgeService.getKdIdsByKnowledgeInfoIds(infoEntity.getKnowledgeIds());
            //知识库超纲问题配置
            KnowledgeSuperclassProblemConfig knowledgeSuperclassProblemConfig = new KnowledgeSuperclassProblemConfig();
            knowledgeSuperclassProblemConfig.setKnowledgeResponseType(infoEntity.getKnowledgeResponseType());
            knowledgeSuperclassProblemConfig.setKnowledgeCustomResponse(infoEntity.getKnowledgeCustomResponse());

            //数据库配置
            Integer[] databaseIds = infoEntity.getKnowledgeDatabaseIds();

            // 构造对话参数
            List<Message> messages = buildMessages(dialogsId, agentId, userBaseEntity.getUserId(), input);

            // 保存用户输入记录
            Long inputTimestamp = System.currentTimeMillis();
            BizAgentApplicationDialoguesRecordEntity inputRecord = new BizAgentApplicationDialoguesRecordEntity();
            inputRecord.setAgentId(agentId);
            inputRecord.setMemberId(userBaseEntity.getUserId());
            inputRecord.setContent(input);
            inputRecord.setImageUrl(imageUrl);
            inputRecord.setDialogsId(dialogsId);
            inputRecord.setRole(AgentApplicationDialoguesRecordConstants.ROLE.USER);
            inputRecord.setTimestamp(inputTimestamp);
            bizAgentApplicationDialoguesRecordService.save(inputRecord);

            //配置对话function
            List<Tool> tools = AgentApplicationTools.buildFunctionConfig(infoEntity.getVariableStructure(), infoEntity.getIsLongMemory(), dialogsId, agentId, infoEntity.getUnitIds(), infoEntity.getIsDocumentParsing());

            // 获取图片
//            List<String> imageUrls = AgentApplicationTools.buildImageUrls(imageUrl);

            //记录输出时间戳
            BizAgentApplicationDialoguesRecordEntity outputRecord = new BizAgentApplicationDialoguesRecordEntity();
            outputRecord.setRole(AgentApplicationDialoguesRecordConstants.ROLE.ASSISTANT);
            outputRecord.setAgentId(infoEntity.getAgentId());
            outputRecord.setDialogsId(dialogsId);
            outputRecord.setMemberId(userBaseEntity.getUserId());
            outputRecord.setTimestamp(System.currentTimeMillis());

            //计算扣分数
            // 判断是否调用function
            CheckPluginUseEntity checkPluginUseEntity = AgentApplicationTools.checkPluginUse(messages, tools, fileUrls);
            Long pointDeductionNum = pointDeductionRulesService.calculatePointDeductionNum(infoEntity.getLargeModel(), infoEntity.getCommunicationTurn(), checkPluginUseEntity.getDeductionTools());
            AgentUseModifyEventInfo agentUseModifyEventInfo = new AgentUseModifyEventInfo();
            agentUseModifyEventInfo.setAgentId(agentId);
            agentUseModifyEventInfo.setIsPublish(CommonConstant.IsDeleted.Y);
            Long deducted = infoEntity.getMemberId().longValue() == 0L ? userBaseEntity.getUserId() : infoEntity.getMemberId();
            try {
                reduceSn = memberEquityService.reducePoint(deducted, pointDeductionNum, ModifyEventEnum.use, agentUseModifyEventInfo);
            } catch (Exception e) {
                if (deducted.equals(userBaseEntity.getUserId())) {
                    throw new I18nMessageException("equity/not.enough.points");
                } else {
                    throw new I18nMessageException("equity/creator.points.cannot.be.used");
                }
            }
            //对话
            AgentResultEntity agentResultEntity = agentApplicationService.callAgentApplication(agentId, dialogsId, infoEntity.getLargeModel(),
                    infoEntity.getAgentSystem(), kdIdList.toArray(new Integer[0]), databaseIds, infoEntity.getCommunicationTurn(),
                    infoEntity.getTopP(), infoEntity.getTemperature(), messages, tools, checkPluginUseEntity.getFunctionCallResult(), true,
                    infoEntity.getKnowledgeSimilarity(), infoEntity.getKnowledgeNResult(), KnowledgeSearchTypeEnum.valueOf(infoEntity.getKnowledgeSearchType()),
                    knowledgeSuperclassProblemConfig, httpServletResponse);

            //保存对话记录
            if (StringUtils.isNotBlank(agentResultEntity.getMessage())) {
                outputRecord.setContent(agentResultEntity.getMessage());
                outputRecord.setReasoningContent(agentResultEntity.getReasoningContent());
                bizAgentApplicationDialoguesRecordService.save(outputRecord);
            }
            //数据采集
            if (StringUtils.isBlank(channel)) {
                channel = DataAnalyzeChannelEnum.link_share.getChannel();
            }
            dataAnalyzeReportService.dataReport(agentId, DataAnalyzeChannelEnum.valueOf(channel), userBaseEntity.getUserId(), deducted, pointDeductionNum);
        } catch (Exception e) {
            memberEquityService.rollbackPoint(reduceSn);
            throw new BusinessException(e.getMessage());
        }
    }


    @Override
    public List<String> createContinueQuestions(String input) {
        BizAgentApplicationGcConfigEntity configEntity = bizAgentApplicationGcConfigService.getByConfigCode(AgentApplicationGCConfigConstants.AGENT_CONTINUE_QUESTIONS);
        if (null == configEntity) {
            throw new I18nMessageException("exception/create.[opening.statement].configuration.does.not.exist");
        }


        String configSystem = configEntity.getConfigSystem();
        configSystem = configSystem.replace("${input}", input);
        MultiContent systemMultiContent = new MultiContent();
        systemMultiContent.setText(configSystem);
        systemMultiContent.setType("text");
        List<MultiContent> multiContents = new ArrayList<>();
        multiContents.add(systemMultiContent);
        Message message = new Message();
        message.setContent(multiContents);
        message.setRole(LLMRoleEnum.USER.getRole());


        LargeModelResponse largeModelResponse = new LargeModelResponse();
        largeModelResponse.setModel(configEntity.getLargeModel());
        largeModelResponse.setMessages(new ArrayList<Message>() {{
            add(message);
        }}.toArray(new Message[1]));
        largeModelResponse.setTopP(configEntity.getTopP());
        largeModelResponse.setUser("POC-CONTINUE-QUESTIONS");
        LargeModelDemandResult largeModelDemandResult = llmService.chat(largeModelResponse);
        if (largeModelDemandResult == null || !"0".equals(largeModelDemandResult.getCode())) {
            logger.error("continue question error ,largeModelDemandResult:{} , largeModelResponse:{}", largeModelDemandResult != null ? largeModelDemandResult.toString() : StringUtils.EMPTY
                    , largeModelResponse);
            return null;
        }
        String res = largeModelDemandResult.getMessage();
        int start = res.lastIndexOf("[");
        int end = res.lastIndexOf("]");
        return JsonUtils.deSerialize(res.substring(start, end + 1), new TypeReference<List<String>>() {
        }.getType());
    }

    @Override
    public List<String> getRecommendQuestions(String xlang, Long memberId) {
        if (StringUtils.isBlank(xlang)) {
            xlang = XLangConstant.ZH_CN;
        }

        String redisKey = AGENT_APPLICATION_RECOMMEND_QUESTIONS + xlang;

        if (!redisService.hasKey(redisKey)) {
            synchronized (AgentApplicationService.class) {
                if (!redisService.hasKey(redisKey)) {
                    createRecommendQuestion();
                }
            }
        }

        long size = redisService.lGetListSize(redisKey);
        if (size < 3) {
            redisService.del(redisKey);
            return null;
        }

        List<Object> lastRecomendIdSet = null;

        // 获取用户上一次的推荐话题ID，并在本次不重复获取该ID
        if (memberId != null) {
            String lastRecommendQuestionRedisKey = MEMBER_RECOMMEND_QUESTIONS_LAST + memberId;
            if (redisService.hasKey(lastRecommendQuestionRedisKey)) {
                lastRecomendIdSet = redisService.lGet(lastRecommendQuestionRedisKey, 0, -1);
            }
        }
        Set<Long> indexSet = new HashSet<>(3);
        SecureRandom secureRandom = new SecureRandom();

        do {
            long id = secureRandom.nextInt((int) size);
            if (CollectionUtils.isNotEmpty(lastRecomendIdSet) && lastRecomendIdSet.stream().anyMatch(v -> String.valueOf(v).equals(String.valueOf(id)))) {
                continue;
            }
            indexSet.add(id);
        } while (indexSet.size() < 3);

        List<String> result = new ArrayList<>(3);
        List<Object> recordIds = new ArrayList<>(3);
        for (Long index : indexSet) {
            Object str = redisService.lGetIndex(redisKey, index);
            result.add(str.toString());
            recordIds.add(index);
        }

        //更新记录
        if (memberId != null) {
            String lastRecommendQuestionRedisKey = MEMBER_RECOMMEND_QUESTIONS_LAST + memberId;
            redisService.del(lastRecommendQuestionRedisKey);
            redisService.lSet(lastRecommendQuestionRedisKey, recordIds, 10 * 60);
        }
        return result;
    }


    @Override
    public void createRecommendQuestion() {
        createCNQuestion();
        createENQuestion();
    }


    @Override
    public List<MemberCollectQueryItem> getCollectedApplications(Long memberId, PagingInfo pagingInfo) {
        MemberCollectQueryCondition condition = new MemberCollectQueryCondition();
        condition.setMemberId(memberId);
        condition.setIsCollect(CommonConstant.YOrN.Y);
        List<MemberCollectQueryItem> memberCollectQueryItems = bizMemberAgentApplicationCollectService.queryMemberCollect(condition, pagingInfo);
        return memberCollectQueryItems;
    }

    @Override
    public String autoPlayByAgentId(Long memberId, String agentId) {
        String result = CommonConstant.YOrN.N;
        String redisKey = AGENT_APPLICATION_AUTO_PLAY + memberId + ":" + agentId;
        if (redisService.hasKey(redisKey)) {
            result = (String) redisService.get(redisKey);
        } else {
            BizAgentApplicationPublishEntity publishEntity = bizAgentApplicationPublishService.getByAgentId(agentId);
            if (publishEntity == null) {
                throw new I18nMessageException("exception/application.does.not.exist");
            }
            VoiceConfig voiceConfig = publishEntity.getVoiceConfig();
            if (voiceConfig != null) {
                String isAutoPlay = voiceConfig.getDefaultOpen();
                result = isAutoPlay;
            }
            redisService.set(redisKey, result, 30 * 60 * 60 * 24);
        }
        return result;
    }

    @Override
    public String enableAutoPlay(Long memberId, String agentId, String autoPlay) {
        BizAgentApplicationPublishEntity publishEntity = bizAgentApplicationPublishService.getByAgentId(agentId);
        if (publishEntity == null) {
            throw new I18nMessageException("exception/application.does.not.exist");
        }
        String redisKey = AGENT_APPLICATION_AUTO_PLAY + memberId + ":" + agentId;
        redisService.set(redisKey, autoPlay, 30 * 60 * 60 * 24);
        return autoPlay;
    }

    @Override
    public BizAgentApplicationApiProfileEntity getApiProfile(Long memberId) {
        BizAgentApplicationApiProfileEntity agentApplicationApiProfileEntity = bizAgentApplicationApiProfileService.getByMemberId(memberId);
        if (null == agentApplicationApiProfileEntity) {
            // 如果没有配置API，则初始化一个默认的API配置
            agentApplicationApiProfileEntity = bizAgentApplicationApiProfileService.initProfile(memberId);
        }
        return agentApplicationApiProfileEntity;
    }

    @Override
    public BizAgentApplicationApiProfileEntity resetApiProfile(Long memberId) throws Exception {
        return bizAgentApplicationApiProfileService.resetProfile(memberId);
    }


    @Override
    public List<AgentApplicationIndexPluginQueryItem> getHomePlugins(String search) {
        AgentApplicationIndexPluginQueryCondition condition = new AgentApplicationIndexPluginQueryCondition();
        condition.setSearch(search);
        return bizAgentApplicationIndexPluginService.agentApplicationIndexPluginQuery(condition);
    }

    private void createCNQuestion() {
        Message message = new Message();
        message.setRole(LLMRoleEnum.USER.getRole());
        message.setContent("请你充当一个话题生成器，结合百度热榜数据，用json格式生成15条开放和引导式的推荐话题给我，我需要用于向AI提问问题，参考格式[\"话题内容\",\"话题N内容\"]，要求1.避免涉及敏感或争议性过强的话题，以确保问题的中立性和客观性。2.只要求生成话题，不需要旁白");

        Message[] messages = new Message[]{message};
        LargeModelResponse largeModelResponse = new LargeModelResponse();
        largeModelResponse.setModel("ERNIE-4.0-8K");
        largeModelResponse.setMessages(messages);
        LargeModelDemandResult largeModelDemandResult = llmService.chat(largeModelResponse);

        if (largeModelDemandResult == null || !"0".equals(largeModelDemandResult.getCode())) {
            throw new I18nMessageException("exception/failed.to.generate.recommendation.question");
        }
        String res = largeModelDemandResult.getMessage();
        int start = res.lastIndexOf("[");
        int end = res.lastIndexOf("]");
        List<Object> questions = JsonUtils.deSerialize(res.substring(start, end + 1), new TypeReference<List<Object>>() {
        }.getType());

        redisService.del(AGENT_APPLICATION_RECOMMEND_QUESTIONS + "zh-cn");
        redisService.lSet(AGENT_APPLICATION_RECOMMEND_QUESTIONS + "zh-cn", questions);

        List<Object> traditionalQuestion = questions.stream().map(q -> ZhConverterUtil.convertToTraditional(q.toString())).collect(Collectors.toList());
        redisService.del(AGENT_APPLICATION_RECOMMEND_QUESTIONS + "zh-tw");
        redisService.lSet(AGENT_APPLICATION_RECOMMEND_QUESTIONS + "zh-tw", traditionalQuestion);
    }

    private void createENQuestion() {
        Message message = new Message();
        message.setRole(LLMRoleEnum.USER.getRole());
        message.setContent("\n" +
                "Please act as a topic generator, combined with Baidu hot list data, generate 15 open and guided recommended topics for me in json format, I need to ask AI questions, refer to the format [\" Topic content \",\" Topic N content \"], requirement 1.2. Only topic generation is required, no narration is required,user english");

        Message[] messages = new Message[]{message};
        LargeModelResponse largeModelResponse = new LargeModelResponse();
        largeModelResponse.setModel("ERNIE-4.0-8K");
        largeModelResponse.setMessages(messages);
        LargeModelDemandResult largeModelDemandResult = llmService.chat(largeModelResponse);

        if (largeModelResponse == null || !"0".equals(largeModelDemandResult.getCode())) {
            throw new I18nMessageException("exception/failed.to.generate.recommendation.question");
        }
        String res = largeModelDemandResult.getMessage();
        int start = res.lastIndexOf("[");
        int end = res.lastIndexOf("]");
        List<Object> questions = JsonUtils.deSerialize(res.substring(start, end + 1), new TypeReference<List<Object>>() {
        }.getType());

        redisService.del(AGENT_APPLICATION_RECOMMEND_QUESTIONS + "en");
        redisService.lSet(AGENT_APPLICATION_RECOMMEND_QUESTIONS + "en", questions);
    }

    private List<Message> buildMessages(String dialogsId, String agentId, Long userId, String input) throws Exception {
        List<Message> messages = new ArrayList<>();
        BizAgentApplicationDialoguesRecordEntity recordEntity = new BizAgentApplicationDialoguesRecordEntity();
        recordEntity.setDialogsId(dialogsId);
        recordEntity.setMemberId(userId);
        recordEntity.setAgentId(agentId);
        List<BizAgentApplicationDialoguesRecordEntity> recordEntities = bizAgentApplicationDialoguesRecordService.findByExample(recordEntity, null);
        if (CollectionUtils.isNotEmpty(recordEntities)) {
            for (BizAgentApplicationDialoguesRecordEntity entity : recordEntities) {
                Message message = new Message();
                message.setContent(entity.getContent());
                message.setRole(entity.getRole());
                messages.add(message);
            }
        }

        //判断最后是否为User，若是则删除。
        if (CollectionUtils.isNotEmpty(messages)) {
            if (LLMRoleEnum.USER.getRole().equals(messages.get(messages.size() - 1).getRole())) {
                Long recordId = recordEntities.get(recordEntities.size() - 1).getId();
                bizAgentApplicationDialoguesRecordService.deletedById(recordId);
                messages.remove(messages.size() - 1);
            }
        }


        // 用户输入
        Message message = new Message();
        message.setContent(input);
        message.setRole(LLMRoleEnum.USER.getRole());
        messages.add(message);
        logger.info("--------- Build Messages dialogsId:{},agentId:{},messages:{}--------------", dialogsId, agentId, messages);
        return messages;
    }
}
