package com.ruoyi.common.utils.jep;

import com.ruoyi.project.business.dto.JEPCustomFunction.FindFunction;
import com.ruoyi.project.business.dto.JEPCustomFunction.IFSFunction;
import com.ruoyi.project.business.dto.JEPCustomFunction.MaxFunction;
import com.ruoyi.project.business.dto.JEPCustomFunction.MinFunction;
import com.ruoyi.project.business.enums.CalculateResultEnum;
import com.ruoyi.project.business.enums.JEPFuncEnum;
import com.ruoyi.project.business.enums.JEPLogicEnum;
import com.ruoyi.project.business.enums.JEPSplitEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.math.NumberUtils;
import org.nfunk.jep.JEP;
import org.nfunk.jep.function.If;
import org.nfunk.jep.function.Sum;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Slf4j
public class JEPUtils {

    public static final String CALCULATE_RESULT = "result";

    public static final String AFTER_ANALYSIS_FORMULA = "analysisFormula";
    public static final String AFTER_ENCIPHER_PARAM = "afterEncipherParams";

    public static final String AFTER_ENCIPHER_FUNC = "afterEncipherFunc";

    public static final String AFTER_ANALYSIS_FUNC = "analysisFunc";

    public static final String AFTER_ANALYSIS_PARAM = "analysisParam";

    public static final String CALCULATE_VALUE = "calculateValue";


    Map<String, Object> map = new HashMap<String, Object>();

    public static Map<String, Object> calculate(String formula, Map<String, Object> map, Boolean isDebug) {
        Map<String, Object> result = new HashMap<>();
        List<String> encipherdList = new ArrayList<>();

        JEP jep = new JEP();
        //加入目前支持的函数
        jep.addFunction("SUM", new Sum());
        jep.addFunction("IF", new If());
        jep.addFunction("MAX", new MaxFunction());
        jep.addFunction("MIN", new MinFunction());
        jep.addFunction("FIND", new FindFunction());
        jep.addFunction("IFS", new IFSFunction());
        //通过 `$@$` 获取方法
        List<String> funcList = getFuncShort(formula);
        for (String func : funcList) {
            formula = formula.replace(JEPSplitEnum.FUNC_SPLIT_FLAG.getFuncKey(), "");
            func = func.replace(JEPSplitEnum.FUNC_SPLIT_FLAG.getFuncKey(), "");
            JEPFuncEnum jEPFuncEnum = JEPFuncEnum.fromByKey(func);
            formula = formula.replace(func, jEPFuncEnum.getFuncValue());
        }
        // 匹配 逻辑符号进行替换
        formula = formula.replace(JEPLogicEnum.OR.getLogicKey(), JEPLogicEnum.OR.getLogicValue());
        formula = formula.replace(JEPLogicEnum.AND.getLogicKey(), JEPLogicEnum.AND.getLogicValue());


        for (String param : map.keySet()) {
            if (!isDebug) {
                log.info("加密后的入参：{}", JEPSplitEnum.PARAM_SPLIT_FLAG.getFuncKey() + param + JEPSplitEnum.PARAM_SPLIT_FLAG.getFuncKey());
                encipherdList.add(JEPSplitEnum.PARAM_SPLIT_FLAG.getFuncKey() + param + JEPSplitEnum.PARAM_SPLIT_FLAG.getFuncKey());
                jep.addVariable(JEPSplitEnum.PARAM_SPLIT_FLAG.getFuncKey() + param + JEPSplitEnum.PARAM_SPLIT_FLAG.getFuncKey(), map.get(param));
            } else {
                log.info("加密后的入参：{}", param);
                encipherdList.add(param);
                String paramStr = (String) map.get(param);
                boolean isNumberFlag = NumberUtils.isCreatable(paramStr);
                jep.addVariable(param, isNumberFlag ? Double.parseDouble(paramStr) : paramStr);
                log.info("入参：{}", param);
                log.info("入参值：{}", isNumberFlag ? Double.parseDouble(paramStr) : paramStr);
            }
        }

        jep.parseExpression(formula);
        log.info("解析后的公式：{}", formula);
        log.info("解析后的公式：{}", jep.getValue());

        if (Double.isNaN(jep.getValue()) || jep.hasError()) {
            result.put(CALCULATE_RESULT, CalculateResultEnum.NG.getValue());
        }
        result.put(AFTER_ANALYSIS_FORMULA, formula);
        result.put(AFTER_ENCIPHER_PARAM, encipherdList);
        result.put(CALCULATE_VALUE, jep.getValue());
        return result;

    }


    public static List<String> getParam(String str) {
        List<String> matchList = new ArrayList<>();
        String regx = "\\$_\\$\\s*(.*?)\\s*\\$_\\$";
        Pattern pattern = Pattern.compile(regx);
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()) {
            matchList.add(matcher.group(1));
        }
        return matchList;
    }

    public static List<String> getFuncShort(String str) {
        List<String> matchList = new ArrayList<>();
        String regx = "\\$@\\$([^\\$]+)\\$@\\$";
        Pattern pattern = Pattern.compile(regx);
        Matcher matcher = pattern.matcher(str);

        // 查找匹配的内容并输出
        while (matcher.find()) {
            String content = matcher.group();
            matchList.add(content);
        }
        return matchList;
    }

    public static Map<String, Object> analysisFormula(String formula) {
        Map<String, Object> result = new HashMap<>();
        //根据 $_$ 获取参数
        List<String> paramList = getParam(formula);
        //根据 $@$ 获取方法
        List<String> funcList = getFuncShort(formula);


        //替换掉其中的 $_$、$@$ 按照长度 倒序排序
        if (paramList.size() > 0) {
            paramList = paramList.stream().map(item -> item.replace(JEPSplitEnum.PARAM_SPLIT_FLAG.getFuncKey(), "")).collect(Collectors.toList());
        }
        if (funcList.size() > 0) {
            funcList = funcList.stream().map(item -> item.replace(JEPSplitEnum.FUNC_SPLIT_FLAG.getFuncKey(), "")).collect(Collectors.toList());
        }

        paramList.sort((o1, o2) -> o2.length() - o1.length());
        result.put(AFTER_ANALYSIS_PARAM, paramList);
        funcList.sort((o1, o2) -> o2.length() - o1.length());
        result.put(AFTER_ANALYSIS_FUNC, funcList);
        for (String s : funcList) {
            JEPFuncEnum funcEnum = JEPFuncEnum.fromByKey(s);
            formula = formula.replace(JEPSplitEnum.FUNC_SPLIT_FLAG.getFuncKey(), "").replace(funcEnum.getFuncKey(), funcEnum.getFuncValue());

        }

        formula = formula.replace(JEPSplitEnum.PARAM_SPLIT_FLAG.getFuncKey(), "");

        result.put(AFTER_ANALYSIS_FORMULA, formula);
        return result;
    }

}
