package com.syjy.calculate.service;

import com.googlecode.aviator.AviatorEvaluator;
import com.syjy.calculate.entity.*;
import com.syjy.calculate.repository.CalculationFormulaRepository;
import com.syjy.calculate.util.Common;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.*;

/**
 * 计算准确率、合格率
 *
 * @author zcl
 * @version 1.0
 * @since 2022/8/8 9:30
 */
@Service
@Slf4j
public class AccuracyPassRateCalculateService {

    @Autowired
    private CalculationFormulaRepository calculationFormulaRepository;
    @Autowired
    private Common common;

    /**
     * 计算准确率、合格率
     *
     * @param calculateRequest
     * @return
     */
    public CalculateResult calculate(CalculateRequest calculateRequest) {
        log.info("--------------------准确率计算开始--------------------");
        // 根据类型从数据库中查出公式列表
        CalculationFormula calculationFormula = calculationFormulaRepository.findByTypeAndProvince(CalculateResult.FORMULA, calculateRequest.getFormulaType(), calculateRequest.getProvince(), calculateRequest.getElectricType(), calculateRequest.getStationCode());
        if (calculationFormula == null || calculationFormula.getFormula() == null) {
            // 未匹配到公式
            return CalculateResult.error(CalculateResult.MSG_NO_FORMULA);
        }
        // 对细则进行校验
        CalculateResult checkedDataResult = checkDataRules(calculateRequest);
        // 如果细则校验失败，返回细则结果
        if (!String.valueOf(checkedDataResult.get(CalculateResult.CODE_TAG)).equals(CalculateResult.Type.SUCCESS.value())) {
            return checkedDataResult;
        }
        // 获取细则校验过的数据
        List<Map<String, Object>> checkedData = (List<Map<String, Object>>) checkedDataResult.get(CalculateResult.DATA_TAG);
        if (checkedData == null || checkedData.size() == 0) {
            return CalculateResult.error(CalculateResult.MSG_CALCULATE_DATA_NULL);
        }

        // 获取公式
        String formula = calculationFormula.getFormula();
        // 初始化公式并获取缓存的公式
        String scriptName = common.initFormula(formula,calculateRequest.getProvince(),calculateRequest.getFormulaType(),calculateRequest.getElectricType(),CalculateResult.FORMULA);

        // 上下文
        Map<String, Object> env = new HashMap<>();
        // 将需要计算的list数据放入上下文
        env.put(CalculateResult.LIST, checkedData);
        env.put(CalculateResult.CAPACITY, calculateRequest.getElectricCapacity());
        env.put(CalculateResult.SIGN, calculateRequest.getSign());

        // 获取最大开机容量
        List<CalculationInfo> calculationInfoList = calculateRequest.getCalculationInfoList();
        calculationInfoList.sort(Comparator.comparing(CalculationInfo::getOpenCapacity).reversed());
        BigDecimal maxOpenCapacity = calculationInfoList.get(0).getOpenCapacity();
        env.put(CalculateResult.MAX_OPEN_CAPACITY, maxOpenCapacity);

        Map<String, Object> calculateRequestMap = new HashMap<>();
        // bean 转map
        BeanMap beanMap = BeanMap.create(calculateRequest);
        for (Object key : beanMap.keySet()) {
            calculateRequestMap.put(String.valueOf(key), beanMap.get(key));
        }
        // 执行计算并得出结果
        try {
            // 获取计算结果
            Object executeResult = AviatorEvaluator.getInstance().getCachedExpressionByKey(scriptName).execute(env);
            String result = String.valueOf(executeResult);
            // 过滤计算结果
            if(calculationFormula.getIsRate().equals(CalculateResult.STR_TRUE)){
                result = filterResult(executeResult);
            }
            log.info("--------------------准确率计算结束--------------------");
            return CalculateResult.success(CalculateResult.MSG_CALCULATE_SUCCESS, result);
        } catch (Exception e) {
            e.printStackTrace();
            log.info("--------------------准确率计算结束--------------------");
            return CalculateResult.error(CalculateResult.MSG_CALCULATE_FAIL + e.toString());
        }
    }

    /**
     * 过滤结果 <0=0  >100=100
     *
     * @param result 过滤前的结果
     * @return 过滤后的结果
     */
    private String filterResult(Object result) {
        // 如果返回的不是数值类型,则不用过滤，直接返回
        if ((result instanceof String) || (result instanceof Boolean)) {
            return String.valueOf(result);
        }
        BigDecimal resultBig = new BigDecimal(String.valueOf(result));
        //当结果为负数时，说明偏差过大，准确率为0
        if (resultBig.compareTo(BigDecimal.ZERO) == -1 || resultBig.compareTo(BigDecimal.ZERO) == 0) {
            log.warn("结果为负数：" + resultBig + "自动转换为0%");
            resultBig = BigDecimal.ZERO;
        }
        // 如果结果大于1，则准确率设为100
        if (resultBig.compareTo(BigDecimal.ONE) == 1) {
            log.warn("结果大于100%：" + resultBig + "自动转换为100%");
            resultBig = BigDecimal.ONE;
        }
        // 对数据*100
        resultBig = resultBig.multiply(CalculateResult.ONE_HUNDRED);
        // 对数据进行四舍五入
        resultBig = resultBig.setScale(2, BigDecimal.ROUND_HALF_UP);
        // 数据加上百分号
        return resultBig + CalculateResult.PERCENT;
    }

    /**
     * 根据细则过滤数据
     *
     * @param calculateRequest
     * @return
     */
    private CalculateResult checkDataRules(CalculateRequest calculateRequest) {
        // 对入参进行校验
        if (calculateRequest == null) {
            return CalculateResult.error(CalculateResult.MSG_REQUEST_NULL);
        }
        // 公式类型
        String formulaType = calculateRequest.getFormulaType();
        if ("".equals(formulaType) || formulaType == null) {
            return CalculateResult.error(CalculateResult.MSG_FORMULA_TYPE_NULL);
        }
        // 省调
        String province = calculateRequest.getProvince();
        if ("".equals(province) || province == null) {
            return CalculateResult.error(CalculateResult.MSG_PROVINCE_NULL);
        }

        // 场站编码
        String stationCode = calculateRequest.getStationCode();
        if ("".equals(stationCode) || stationCode == null) {
            return CalculateResult.error(CalculateResult.MSG_STATION_CODE_NULL);
        }

        // 场站类型
        String electricType = calculateRequest.getElectricType();
        if ("".equals(electricType) || electricType == null) {
            return CalculateResult.error(CalculateResult.MSG_ELECTRIC_TYPE_NULL);
        }
        // 获取被校验数据
        List<CalculationInfo> calculationInfoList = calculateRequest.getCalculationInfoList();
        // 计算数据为空
        if (calculationInfoList == null || calculationInfoList.size() == 0) {
            return CalculateResult.error(CalculateResult.MSG_CALCULATE_DATA_NULL);
        }

        // 装机容量校验
        if (calculateRequest.getElectricCapacity() == null || calculateRequest.getElectricCapacity().compareTo(BigDecimal.ZERO) == 0) {
            return CalculateResult.error(CalculateResult.MSG_CAP_NULL);
        }

        // 循环判断实际功率/预测功率、开机容量
        for (int i = 0; i < calculationInfoList.size(); i++) {
            // 如果 实际功率/可用功率/预测功率为null 则剔除此条
            if (calculationInfoList.get(i).getAbleValue() == null || calculationInfoList.get(i).getForecastAbleValue() == null || calculationInfoList.get(i).getRealValue() == null) {
                calculationInfoList.remove(i);
            }
            // 如果开机容量 null 返回
            if (calculationInfoList.get(i).getOpenCapacity() == null || calculationInfoList.get(i).getOpenCapacity().compareTo(BigDecimal.ZERO) == 0) {
                return CalculateResult.error(CalculateResult.MSG_CAP_NULL);
            }
        }

        // 将数据转为List Map 格式
        List<Map<String, Object>> calculationInfoListMap = getCalculationInfoList(calculationInfoList, calculateRequest.getElectricCapacity());
        // 获取细则公式
        CalculationFormula rulesCalculationFormula = calculationFormulaRepository.findByTypeAndProvince(CalculateResult.RULES, formulaType, province, electricType,stationCode);
        if (rulesCalculationFormula == null || rulesCalculationFormula.getFormula() == null) {
            // 未匹配到细则文件，则不进行细则校验，返回数据
            return CalculateResult.success(calculationInfoListMap);
        }

        String formula = rulesCalculationFormula.getFormula();
        // 初始化公式并获取缓存的公式
        String scriptName = common.initFormula(formula,calculateRequest.getProvince(),calculateRequest.getFormulaType(),calculateRequest.getElectricType(),CalculateResult.RULES);
        // 通过校验的数据
        List<Map<String, Object>> passList = new ArrayList<>();
        // 循环数据，对数据进行校验
        for (Map<String, Object> calculationMap : calculationInfoListMap) {
            // 循环公式，对每条功率信息进行细则处理
            try {
                Map<String, Object> checkResult = (HashMap<String, Object>) AviatorEvaluator.getInstance().getCachedExpressionByKey(scriptName).execute(calculationMap);
                // 如果返回结果是忽略，则结束本次循环
                if (String.valueOf(checkResult.get(CalculateResult.CODE_TAG)).equals(CalculateResult.IGNORE)) {
                    continue;
                    // 如果返回错误，直接返回结果
                } else if (String.valueOf(checkResult.get(CalculateResult.CODE_TAG)).equals(CalculateResult.ERROR)) {
                    String resultValue = String.valueOf(checkResult.get(CalculateResult.DATA_TAG));
                    String msg = String.valueOf(checkResult.get(CalculateResult.MSG_TAG));
                    return CalculateResult.error(msg, resultValue);
                }
                // 校验成功，将通过的数据放入passList
                passList.add(calculationMap);
            } catch (Exception e) {
                log.error(CalculateResult.MSG_DATA_CHECK_ERROR + e.toString());
                return CalculateResult.error(CalculateResult.MSG_DATA_CHECK_ERROR);
            }
        }
        // 校验成功，返回待计算的数据
        return CalculateResult.success(passList);
    }

    /**
     * 格式化被校验数据
     *
     * @return
     */
    private List<Map<String, Object>> getCalculationInfoList(List<CalculationInfo> calculationInfoList, BigDecimal electricCapacity) {
        List<Map<String, Object>> resultList = new ArrayList<>();
        // 循环每条数据
        for (CalculationInfo calculationInfo : calculationInfoList) {
            // 将bean BeanMap
            BeanMap beanMap = BeanMap.create(calculationInfo);
            Map<String, Object> map = new HashMap<>();
            // 将beanMap转为Map
            for (Object key : beanMap.keySet()) {
                map.put(String.valueOf(key), beanMap.get(key));
            }
            map.put("electricCapacity", electricCapacity);
            resultList.add(map);
        }
        return resultList;
    }
}

