package cn.iocoder.yudao.module.llm.service.async;

import cn.hutool.json.JSONObject;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.llm.controller.admin.dataset.vo.DatasetAnswerRespVO;
import cn.iocoder.yudao.module.llm.controller.admin.dataset.vo.DatasetQuestionRespVO;
import cn.iocoder.yudao.module.llm.dal.dataobject.dataprocesstask.DataProcessTaskDO;
import cn.iocoder.yudao.module.llm.dal.dataobject.dataset.DatasetAnswerDO;
import cn.iocoder.yudao.module.llm.dal.dataobject.dataset.DatasetDO;
import cn.iocoder.yudao.module.llm.dal.dataobject.dataset.DatasetQuestionDO;
import cn.iocoder.yudao.module.llm.dal.mysql.dataprocesstask.DataProcessTaskMapper;
import cn.iocoder.yudao.module.llm.dal.mysql.dataset.DatasetAnswerMapper;
import cn.iocoder.yudao.module.llm.dal.mysql.dataset.DatasetMapper;
import cn.iocoder.yudao.module.llm.dal.mysql.dataset.DatasetQuestionMapper;
import cn.iocoder.yudao.module.llm.service.dataset.DatasetQuestionService;
import cn.iocoder.yudao.module.llm.utils.DataProcessUtil;
import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
public class AsyncDataProcessService {
    @Resource
    private DatasetMapper datasetMapper;
    @Resource
    private DataProcessTaskMapper dataProcessTaskMapper;
    @Resource
    private DatasetQuestionService datasetQuestionService;
    @Resource
    private DatasetQuestionMapper datasetQuestionMapper;
    @Resource
    private DatasetAnswerMapper datasetAnswerMapper;
    private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    private static final SecureRandom RANDOM = new SecureRandom();

    @Async
    public void backups (DataProcessTaskDO dataProcessTask) {
        try {
            // 判断是否备份
            if (dataProcessTask.getDatasetPostId() == null) {
                DatasetDO datasetDO = datasetMapper.selectById(dataProcessTask.getDatasetId());
                DatasetDO newData = BeanUtils.toBean(datasetDO, DatasetDO.class);
                newData.setId(null);
                newData.setDatasetName(datasetDO.getDatasetName() + generateRandomString(4));
                datasetMapper.insert(newData);
                dataProcessTask.setDatasetPostId(newData.getId());
                dataProcessTaskMapper.updateById(dataProcessTask);
            }
            // 删除备份后得副表数据 问题表 和答案表
            datasetQuestionMapper.deleteTrue(dataProcessTask.getDatasetPostId());
            datasetAnswerMapper.deleteTrue(dataProcessTask.getDatasetPostId());

            // 开始数据处理
            startDataProcess(dataProcessTask);

        } catch (Exception e) {
            datasetQuestionMapper.deleteTrue(dataProcessTask.getDatasetPostId());
            datasetAnswerMapper.deleteTrue(dataProcessTask.getDatasetPostId());
            datasetMapper.deleteTrue(dataProcessTask.getDatasetPostId());
            dataProcessTask.setDatasetPostId(null);
            dataProcessTask.setStatus(5);
            dataProcessTaskMapper.updateById(dataProcessTask);

        }

    }

    /**
     * 数据处理
     *
     * @param dataProcessTask 数据处理任务
     */
    private void startDataProcess (DataProcessTaskDO dataProcessTask) {
        log.info(" =========== 数据处理开始 =========== {}", JSON.toJSONString(dataProcessTask));

        // 获取所有问题和回答
        List<DatasetQuestionRespVO> datasetQuestionList = datasetQuestionService.getDatasetQuestionList(dataProcessTask.getDatasetId());
        //        log.info(" *********** 处理前数据: *********** {}", JSON.toJSONString(datasetQuestionList));
        // 所有列表循环处理
        if (!CollectionUtils.isAnyEmpty(datasetQuestionList)) {
            // 获取数据处理配置
            JSONObject options = dataProcessTask.getOptions();

            // 先处理问题，有需要过滤文档的直接把问题和回答都过滤掉
            datasetQuestionList = processQuestion(datasetQuestionList, options);

            // 处理回答
            datasetQuestionList = processAnswer(datasetQuestionList, options);
        }


        if (!CollectionUtils.isAnyEmpty(datasetQuestionList)) {
            datasetQuestionList.forEach(item -> {
                DatasetQuestionDO questionDO = BeanUtils.toBean(item, DatasetQuestionDO.class);
                questionDO.setDatasetId(dataProcessTask.getDatasetPostId());
                questionDO.setId(null);
                questionDO.setQuestion(StringUtils.isEmpty(item.getQuestion()) ? "" : replaceStr(item.getQuestion()));
                questionDO.setCreator(dataProcessTask.getCreator());
                questionDO.setUpdater(dataProcessTask.getUpdater());
                datasetQuestionMapper.insert(questionDO);

                if (!CollectionUtils.isAnyEmpty(item.getDatasetAnswerRespVO())) {
                    item.getDatasetAnswerRespVO().forEach(item1 -> {
                        DatasetAnswerDO answerDO = BeanUtils.toBean(item1, DatasetAnswerDO.class);
                        answerDO.setQuestionId(questionDO.getId());
                        answerDO.setCreator(dataProcessTask.getCreator());
                        answerDO.setUpdater(dataProcessTask.getCreator());
                        answerDO.setAnswer(StringUtils.isEmpty(item1.getAnswer()) ? "" : replaceStr(item1.getAnswer()));
                        answerDO.setId(null);
                        answerDO.setDatasetId(dataProcessTask.getDatasetPostId());
                        datasetAnswerMapper.insert(answerDO);
                    });
                }
            });
        }


        dataProcessTaskMapper.updateStatus(dataProcessTask.getId(), 2);
        log.info(" =========== 数据处理结束 =========== {}", JSON.toJSONString(dataProcessTask));
        //        log.info(" *********** 处理后数据: ************ {}", JSON.toJSONString(datasetQuestionList));
    }

    /**
     * 处理问题
     *
     * @param datasetQuestionList 数据集列表
     * @param options             配置
     * @return 处理后的数据集列表
     */
    private List<DatasetQuestionRespVO> processQuestion (List<DatasetQuestionRespVO> datasetQuestionList, JSONObject options) {
        // 转化为 Map，key 为 id，value 为问题文本
        Map<Long, String> questionMap = datasetQuestionList.stream()
                .collect(Collectors.toMap(DatasetQuestionRespVO::getId, DatasetQuestionRespVO::getQuestion));

        // 对问题进行处理，将处理后的结果存储在 processAfterMap 中
        Map<Long, String> processAfterMap = processTask(questionMap, options);

        // 筛选出在 processAfterMap 中存在对应 id 的元素，更新问题，收集为新列表
        return datasetQuestionList.stream()
                // 过滤掉 processAfterMap 中不存在对应 id 的元素
                .filter(item -> processAfterMap.containsKey(item.getId()))
                // 更新问题
                .peek(item -> item.setQuestion(processAfterMap.get(item.getId())))
                .collect(Collectors.toList());
    }

    /**
     * 处理回答
     *
     * @param datasetQuestionList 数据集列表
     * @param options             配置
     * @return 处理后的数据集列表
     */
    private List<DatasetQuestionRespVO> processAnswer (List<DatasetQuestionRespVO> datasetQuestionList, JSONObject options) {
        // 先去除所有空的回答
        datasetQuestionList.removeIf(item -> com.baomidou.mybatisplus.core.toolkit.CollectionUtils.isEmpty(item.getDatasetAnswerRespVO()));

        // 获取所有回答
        List<DatasetAnswerRespVO> datasetAnswerList = datasetQuestionList.stream()
                .flatMap(item -> item.getDatasetAnswerRespVO().stream())
                .collect(Collectors.toList());

        // 转化为 Map，key 为 id，value 为回答文本
        Map<Long, String> answerMap = datasetAnswerList.stream()
                .collect(Collectors.toMap(DatasetAnswerRespVO::getId, DatasetAnswerRespVO::getAnswer));

        // 回答进行处理，将处理后的结果存储在 processAfterMap 中
        Map<Long, String> processAfterMap = processTask(answerMap, options);

        // 筛选出在 processAfterMap 中存在对应 id 的元素，更新问题，收集为新列表
        datasetAnswerList = datasetAnswerList.stream()
                // 过滤掉 processAfterMap 中不存在对应 id 的元素
                .filter(item -> processAfterMap.containsKey(item.getId()))
                // 更新问题
                .peek(item -> item.setAnswer(processAfterMap.get(item.getId())))
                .collect(Collectors.toList());

        // 赋值回 datasetQuestionList 列表
        List<DatasetAnswerRespVO> finalDatasetAnswerList = datasetAnswerList;

        datasetQuestionList.forEach(question -> {

            List<DatasetAnswerRespVO> updatedAnswers = finalDatasetAnswerList.stream()
                    .filter(answer -> question.getDatasetAnswerRespVO().stream()
                            .anyMatch(originalAnswer -> originalAnswer.getId().equals(answer.getId())))
                    .collect(Collectors.toList());

            question.setDatasetAnswerRespVO(updatedAnswers);
        });

        return datasetQuestionList;

    }

    /**
     * 替换字符串
     *
     * @param text 文本
     * @return 处理后的文本
     */
    public static String replaceStr (String text) {
        return text.replace("[", "").replace("]", "");
    }

    public static String generateRandomString (int length) {
        StringBuilder sb = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            int index = RANDOM.nextInt(CHARACTERS.length());
            sb.append(CHARACTERS.charAt(index));
        }
        return sb.toString();
    }


    /**
     * 处理任务
     *
     * @param texts   文本列表
     * @param options 配置
     * @return 处理后的文本列表
     */
    public static Map<Long, String> processTask (Map<Long, String> texts, JSONObject options) {
        // 数据处理任务 1 ：异常清洗配置处理
        texts = dataCleaning(texts, options);

        // 数据处理任务 2 ：过滤配置处理
        texts = dataFilter(texts, options);

        // 数据处理任务 3 ：去重配置处理
        texts = dataDistinct(texts, options);

        // 数据处理任务 4 ：去隐私配置处理
        texts = dataAnonymization(texts, options);

        return texts;
    }


    /**
     * 数据处理任务 1 ：异常清洗配置处理
     *
     * @param texts   文本列表
     * @param options 数据处理配置
     * @return 处理后的文本列表
     */
    private static Map<Long, String> dataCleaning (Map<Long, String> texts, JSONObject options) {
        JSONObject optionsA = options.getJSONObject("a");

        Map<Long, String> resultMap = new HashMap<>();
        if (com.baomidou.mybatisplus.core.toolkit.CollectionUtils.isNotEmpty(texts)) {
            for (Map.Entry<Long, String> entry : texts.entrySet()) {
                Long key = entry.getKey();
                String value = entry.getValue();
                if (org.apache.commons.lang3.StringUtils.isNotBlank(value)) {
                    String cleanedValue = dataCleaningDetail(optionsA, value);
                    resultMap.put(key, cleanedValue);
                }
            }
        }

        return resultMap;
    }

    /**
     * 异常清洗配置处理
     *
     * @param options 异常清洗配置
     * @param text    要处理的文本
     * @return 处理后的文本
     */
    public static String dataCleaningDetail (JSONObject options, String text) {

        String result = text;
        // 移除不可见字符 移除 ASCII 中的一些不可见字符, 如 0-32 和 127-160 这两个范围
        JSONObject a_1 = options.getJSONObject("a_1");
        if (a_1.getBool("is_on")) {
            result = DataProcessUtil.removeNonVisibleAsciiChars(result);
        }
        // 移除不可见字符 将不同的 unicode 空格比如  u2008，转成正常的空格
        JSONObject a_2 = options.getJSONObject("a_2");
        if (a_2.getBool("is_on")) {
            result = DataProcessUtil.convertUnicodeSpacesToNormalSpaces(result);
        }
        // 移除不可见字符 去除乱码和无意义的 unicode
        JSONObject a_3 = options.getJSONObject("a_3");
        if (a_3.getBool("is_on")) {
            result = DataProcessUtil.removeNonPrintableUnicodeChars(result);
        }
        // 繁体转简体 繁体转简体，如“不經意，妳的笑容”清洗成“不经意，你的笑容”
        JSONObject a_4 = options.getJSONObject("a_4");
        if (a_4.getBool("is_on")) {
            result = DataProcessUtil.traditionalToSimplified(result);
        }
        // 去除网页标识符 移除文档中的 html 标签，如<html>,<div><p>等
        JSONObject a_5 = options.getJSONObject("a_5");
        if (a_5.getBool("is_on")) {
            result = DataProcessUtil.removeHtmlTags(result);
        }
        // 去除表情 去除文档中的表情，如‘🐰’、‘👵’等
        JSONObject a_6 = options.getJSONObject("a_6");
        if (a_6.getBool("is_on")) {
            result = DataProcessUtil.removeEmojis(result);
        }
        return result;
    }

    /**
     * 数据处理任务 2 ：过滤配置处理
     *
     * @param texts   文本列表
     * @param options 数据处理配置
     * @return 处理后的文本列表
     */
    private static Map<Long, String> dataFilter (Map<Long, String> texts, JSONObject options) {
        JSONObject optionsB = options.getJSONObject("b");
        Map<Long, String> resultMap = new HashMap<>();
        if (com.baomidou.mybatisplus.core.toolkit.CollectionUtils.isNotEmpty(texts)) {
            for (Map.Entry<Long, String> entry : texts.entrySet()) {
                log.info("======================");
                Long key = entry.getKey();
                String value = entry.getValue();
                if (org.apache.commons.lang3.StringUtils.isNotBlank(value)) {
                    // 检查文档的词数目
                    String cleanedValue = dataFilterDetailByWordNumber(optionsB, value);
                    // 使用检测文档次数目后的文本继续进行检测
                    log.info("检测文档次数目后的文本：{}", cleanedValue);
                    // 其他过滤配置
                    boolean shouldFilter = dataFilterDetailByOther(optionsB, cleanedValue);
                    if (!shouldFilter) {
                        resultMap.put(key, cleanedValue);
                    }
                }
            }
        }
        return resultMap;
    }

    /**
     * 过滤配置处理：检查文档的词数目
     *
     * @param options 过滤配置
     * @param text    文本
     * @return 处理后的文本
     */
    public static String dataFilterDetailByWordNumber (JSONObject options, String text) {
        String result = text;
        // 检查文档的词数目：词数目不在指定范围会被过滤掉，如中文，取值范围[1,1000000]（正整数）。
        JSONObject b_1 = options.getJSONObject("b_1");
        if (b_1.getBool("is_on")) {
            Integer num1 = b_1.getInt("num1");
            Integer num2 = b_1.getInt("num2");
            result = DataProcessUtil.filterWords(result, num1, num2).toString();
        }

        return result;
    }

    /**
     * 过滤配置处理：其他过滤配置
     *
     * @param options 过滤配置
     * @param text    文本
     * @return 是否过滤文档
     */
    public static boolean dataFilterDetailByOther (JSONObject options, String text) {
        // 检查文档的字重复率：如果字重复率太高，意味着文档中重复的字太多，文档会被过滤掉，取值范围[0,1]。
        JSONObject b_2 = options.getJSONObject("b_2");
        if (b_2.getBool("is_on")) {
            int threshold = b_2.getInt("num1");
            if (DataProcessUtil.calculateCharacterRepetitionRate(text, threshold)) {
                return true;
            }
        }

        // 检查文档的词重复率：如果词重复率太高，意味着文档中重复的词太多，文档会被过滤掉，取值范围[0,1]。
        JSONObject b_3 = options.getJSONObject("b_3");
        if (b_3.getBool("is_on")) {
            double threshold = b_3.getDouble("num1");
            if (DataProcessUtil.calculateWordRepetitionRate(text, threshold)) {
                return true;
            }
        }

        // 检查文档的特殊字符率：如果特殊字符率太高，意味着文档中特殊字符太多，文档会被过滤掉，取值范围[0,1]。
        JSONObject b_4 = options.getJSONObject("b_4");
        if (b_4.getBool("is_on")) {
            double threshold = b_4.getDouble("num1");
            if (DataProcessUtil.checkSpecialCharacterRate(text, threshold)) {
                log.info("过滤特殊字符检测：{}", text);
                return true;
            }
        }

        // 检查文档的色情暴力词率：如果色情暴力词率太高，文档会被过滤掉，取值范围[0,1]。
        JSONObject b_5 = options.getJSONObject("b_5");
        if (b_5.getBool("is_on")) {
            double threshold = b_5.getDouble("num1");
            if (DataProcessUtil.checkSensitiveWordRate(text, threshold)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 数据处理任务 3 ：去重配置处理
     *
     * @param texts   文本列表
     * @param options 数据处理配置
     * @return 处理后的文本列表
     */
    private static Map<Long, String> dataDistinct (Map<Long, String> texts, JSONObject options) {
        JSONObject optionsC = options.getJSONObject("c");
        Map<Long, String> resultMap = new HashMap<>();
        if (com.baomidou.mybatisplus.core.toolkit.CollectionUtils.isNotEmpty(texts)) {
            List<Long> repeatedKeys = dataDistinctDetail(optionsC, texts);
            // 从原列表中移除相似的
            texts.keySet().removeIf(repeatedKeys::contains);
            // 直接将 剩下的 texts 中的元素添加到 resultMap 中
            resultMap.putAll(texts);
        }
        return resultMap;
    }

    /**
     * 去重配置处理
     *
     * @param texts   文本列表
     * @param options 去重配置
     * @return 需要去重读取的 key(Id) 列表
     */
    private static List<Long> dataDistinctDetail (JSONObject options, Map<Long, String> texts) {
        JSONObject c_1 = options.getJSONObject("c_1");
        List<Long> repeatedKeys = new ArrayList<>();
        if (c_1.getBool("is_on")) {
            Double threshold = c_1.getDouble("num1");
            repeatedKeys = DataProcessUtil.similarityDeduplication(texts, threshold);
        }
        return repeatedKeys;
    }

    /**
     * 数据处理任务 4 ：去隐私配置处理
     *
     * @param texts   文本列表
     * @param options 数据处理配置
     * @return 处理后的文本列表
     */
    private static Map<Long, String> dataAnonymization (Map<Long, String> texts, JSONObject options) {
        JSONObject optionsD = options.getJSONObject("d");

        Map<Long, String> resultMap = new HashMap<>();
        if (com.baomidou.mybatisplus.core.toolkit.CollectionUtils.isNotEmpty(texts)) {
            for (Map.Entry<Long, String> entry : texts.entrySet()) {
                Long key = entry.getKey();
                String value = entry.getValue();
                if (org.apache.commons.lang3.StringUtils.isNotBlank(value)) {
                    String cleanedValue = dataAnonymizationDetail(optionsD, value);
                    resultMap.put(key, cleanedValue);
                }
            }
        }

        return resultMap;
    }

    /**
     * 去隐私配置处理
     *
     * @param options 去隐私配置
     * @param text    要处理的文本
     * @return 处理后的文本
     */
    public static String dataAnonymizationDetail (JSONObject options, String text) {

        String result = text;
        // 去除Email：去除Email地址。
        JSONObject d_1 = options.getJSONObject("d_1");
        if (d_1.getBool("is_on")) {
            result = DataProcessUtil.processFile(result);
        }
        // 去除IP地址：去除IPv4 或者 IPv6 地址。
        JSONObject d_2 = options.getJSONObject("d_2");
        if (d_2.getBool("is_on")) {
            result = DataProcessUtil.removeIPAddresses(result);
        }
        // 去除数字：去除数字和字母数字标识符，如电话号码、信用卡号、十六进制散列等，同时跳过年份和简单数字的实例。
        JSONObject d_3 = options.getJSONObject("d_3");
        if (d_3.getBool("is_on")) {
            result = DataProcessUtil.removeIdentifiers(result);
        }
        return result;
    }
}
