package com.njcn.advance.service.govern.voltage.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.StrPool;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.njcn.advance.enums.AdvanceResponseEnum;
import com.njcn.advance.enums.GovernSchemeEnum;
import com.njcn.advance.pojo.dto.govern.voltage.ProductLineComputeData;
import com.njcn.advance.pojo.param.govern.voltage.CommonSchemeParam;
import com.njcn.advance.pojo.param.govern.voltage.QuickSchemeParam;
import com.njcn.advance.pojo.param.govern.voltage.SgGovernSchemeHistoryParam;
import com.njcn.advance.pojo.po.govern.voltage.*;
import com.njcn.advance.pojo.vo.govern.voltage.*;
import com.njcn.advance.service.govern.voltage.*;
import com.njcn.advance.utils.RadarUtil;
import com.njcn.common.pojo.enums.common.DataStateEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.common.utils.PubUtils;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author hongawen
 */
@Service
@RequiredArgsConstructor
public class GovernSchemeCalcServiceImpl implements IGovernSchemeCalcService {

    private final ISgProductLineService productLineService;

    private final ISgUserService userService;

    private final ISgIncomingLineService incomingLineService;

    private final ISgGovernSchemeParamService sgGovernSchemeParamService;

    private final ISgEventService eventService;

    private final ISgGovernSchemeHistoryDetailService sgGovernSchemeHistoryDetailService;

    private final ISgGovernSchemeHistoryService sgGovernSchemeHistoryService;


    /**
     * 根据前台传递的治理方案的各参数，进行每个治理方案的治理评估
     *
     * @param sgGovernSchemeHistoryParam 方案参数
     */
    @Override
    public SgGovernSchemeHistoryAllVO calc(SgGovernSchemeHistoryParam sgGovernSchemeHistoryParam) {
        SgGovernSchemeHistoryAllVO sgGovernSchemeHistoryAllVO = initInvestmentScheme(sgGovernSchemeHistoryParam);
        //准备暂降数据
        SgProductLine sgProductLine = productLineService.getById(sgGovernSchemeHistoryAllVO.getProductLineId());
        if (Objects.isNull(sgProductLine)) {
            throw new BusinessException(AdvanceResponseEnum.PRODUCT_LINE_DATA_MISS);
        }
        //根据生产线获取该用户名称
        String userName = productLineService.getUserNameByProductId(sgProductLine.getId());
        sgGovernSchemeHistoryAllVO.setUserName(userName);
        sgGovernSchemeHistoryAllVO.setProductLineName(sgProductLine.getName());
        SgIncomingLine sgIncomingLine = incomingLineService.getById(sgProductLine.getIncomingLineId());
        if (Objects.isNull(sgIncomingLine)) {
            throw new BusinessException(AdvanceResponseEnum.INCOMING_LINE_DATA_MISS);
        }
        sgGovernSchemeHistoryAllVO.setInComingLineName(sgIncomingLine.getName());
        sgGovernSchemeHistoryAllVO.getQuick().setIncomingLineId(sgIncomingLine.getName());
        //时间内的电网接入水平（暂降次数）
        String startTime = sgGovernSchemeHistoryParam.getStartTime();
        String endTime = sgGovernSchemeHistoryParam.getEndTime();
        //这种区间计算损失不合适，经过平均后，特征幅值在40%以下，直接被平均为25%，故此处我准备按实际的暂态信息计算
        //区间计算方式：Map<Integer[], Integer> eventMap = eventService.getEventMap(sgIncomingLine.getId(), startTime, endTime);
        List<SgEvent> sgEventList = eventService.getEventData(sgIncomingLine.getId(), startTime, endTime);
        if (CollectionUtil.isEmpty(sgEventList)) {
            throw new BusinessException(AdvanceResponseEnum.EVENT_DATA_MISS);
        }

        //求数据时间区间的年化率
        int days = (int) LocalDateTimeUtil.between(LocalDateTimeUtil.parse(startTime, DatePattern.NORM_DATE_PATTERN), LocalDateTimeUtil.parse(endTime, DatePattern.NORM_DATE_PATTERN)).toDays();
        //只能求一个大致的年化率，默认为一年365天
        double yearRate = 365.0 / (double) days;
        ProductLineComputeData productLineComputeData = productLineService.assginProductLineComputeData(sgGovernSchemeHistoryAllVO.getProductLineId());
        computeProductLineLoss(sgEventList, sgGovernSchemeHistoryAllVO, GovernSchemeEnum.NO_SCHEME, yearRate, productLineComputeData);
        computeProductLineLoss(sgEventList, sgGovernSchemeHistoryAllVO, GovernSchemeEnum.UPS, yearRate, productLineComputeData);
        computeProductLineLoss(sgEventList, sgGovernSchemeHistoryAllVO, GovernSchemeEnum.AVC, yearRate, productLineComputeData);

        //开关快切治理方案单独计算
        Double actionTime = sgGovernSchemeHistoryAllVO.getQuick().getActionTime();
        String incomingLineId = sgGovernSchemeHistoryAllVO.getQuick().getIncomingLineId();
        List<SgEvent> eventQuick = eventService.countEvent4Switch(
                sgEventList,
                sgIncomingLine.getId(),
                incomingLineId,
                startTime,
                endTime,
                actionTime);
        SgGovernSchemeParam schemeParam = new SgGovernSchemeParam();
        schemeParam.setState(DataStateEnum.ENABLE.getCode());
        BeanUtil.copyProperties(sgGovernSchemeHistoryAllVO.getQuick(), schemeParam);
        sgGovernSchemeParamService.save(schemeParam);
        if (CollectionUtil.isEmpty(eventQuick)) {
            SgGovernSchemeHistoryDetailVO quickSgGovernSchemeHistoryDetailVO = new SgGovernSchemeHistoryDetailVO();
            //说明被治理的很好，切到备用线路后，没有暂降事件了
            quickSgGovernSchemeHistoryDetailVO.setEventCount(0);
            quickSgGovernSchemeHistoryDetailVO.setBreakCount(0);
            quickSgGovernSchemeHistoryDetailVO.setLoss(0.0);
            quickSgGovernSchemeHistoryDetailVO.setYearLoss(0.0);
            quickSgGovernSchemeHistoryDetailVO.setType(GovernSchemeEnum.QUICK.getCode());
            quickSgGovernSchemeHistoryDetailVO.setEvaluationBatch(sgGovernSchemeHistoryAllVO.getEvaluationBatch());
            sgGovernSchemeHistoryAllVO.setQuickData(quickSgGovernSchemeHistoryDetailVO);
        } else {
            //说明没有必要切换备用线路，备用线路也存在同时间范围内的暂降
            computeProductLineLoss(eventQuick, sgGovernSchemeHistoryAllVO, GovernSchemeEnum.QUICK, yearRate, productLineComputeData);
        }
        sgGovernSchemeHistoryAllVO.getQuickData().setSchemeParamId(schemeParam.getId());
        //每个方案的损失已经计算完毕，开始准备给前台数据回显
        generateSchemeView(sgGovernSchemeHistoryAllVO, productLineComputeData);
        //从各个方案中找一个最佳方案
        chooseBestScheme(sgGovernSchemeHistoryAllVO);
        //todo...待做的还有文字信息
        return sgGovernSchemeHistoryAllVO;
    }

    @Override
    public SgGovernSchemeHistoryAllVO detail(String id) {
        SgGovernSchemeHistoryAllVO allVO = new SgGovernSchemeHistoryAllVO();
        SgGovernSchemeHistory sgGovernSchemeHistory = sgGovernSchemeHistoryService.getById(id);
        BeanUtil.copyProperties(sgGovernSchemeHistory, allVO);
        //获取生产线、进线、用户名
        SgProductLine sgProductLine = productLineService.getById(sgGovernSchemeHistory.getProductLineId());
        SgUser sgUser = userService.getById(sgProductLine.getUserId());
        SgIncomingLine incomingLine = incomingLineService.getById(sgProductLine.getIncomingLineId());
        allVO.setUserName(sgUser.getUserName());
        allVO.setInComingLineName(incomingLine.getName());
        allVO.setProductLineName(sgProductLine.getName());

        //查询UPS的结果和参数
        LambdaQueryWrapper<SgGovernSchemeHistoryDetail> historyDetailLambdaQueryWrapper = new LambdaQueryWrapper<>();
        historyDetailLambdaQueryWrapper.eq(SgGovernSchemeHistoryDetail::getEvaluationBatch, sgGovernSchemeHistory.getEvaluationBatch())
                .eq(SgGovernSchemeHistoryDetail::getType, GovernSchemeEnum.UPS.getCode());
        SgGovernSchemeHistoryDetail upsResult = sgGovernSchemeHistoryDetailService.getOne(historyDetailLambdaQueryWrapper);
        SgGovernSchemeHistoryDetailVO upsVo = new SgGovernSchemeHistoryDetailVO();
        BeanUtil.copyProperties(upsResult, upsVo);
        copyHistoryVo(upsResult, upsVo);
        if (allVO.getBestSchemeId().equals(upsVo.getId())) {
            allVO.setSchemeType(GovernSchemeEnum.UPS.getCode());
        }
        List<Double> governEffectUPS = new ArrayList<>();
        for (int i = 0; i < allVO.getPayBackPeriod(); i++) {
            governEffectUPS.add(allVO.getLoss() - upsVo.getLoss());
        }
        upsVo.setGovernEffectArray(governEffectUPS);
        allVO.setUpsData(upsVo);
        LambdaQueryWrapper<SgGovernSchemeParam> sgGovernSchemeParamLambdaQueryWrapper = new LambdaQueryWrapper<>();
        sgGovernSchemeParamLambdaQueryWrapper.eq(SgGovernSchemeParam::getId, upsResult.getSchemeParamId());
        SgGovernSchemeParam upsParamOne = sgGovernSchemeParamService.getOne(sgGovernSchemeParamLambdaQueryWrapper);
        CommonSchemeParam upsParam = new CommonSchemeParam();
        BeanUtil.copyProperties(upsParamOne, upsParam);
        allVO.setUps(upsParam);
        //查询AVC的结果和参数
        LambdaQueryWrapper<SgGovernSchemeHistoryDetail> historyDetailLambdaQueryWrapperAVC = new LambdaQueryWrapper<>();
        historyDetailLambdaQueryWrapperAVC.eq(SgGovernSchemeHistoryDetail::getEvaluationBatch, sgGovernSchemeHistory.getEvaluationBatch())
                .eq(SgGovernSchemeHistoryDetail::getType, GovernSchemeEnum.AVC.getCode());
        SgGovernSchemeHistoryDetail avcResult = sgGovernSchemeHistoryDetailService.getOne(historyDetailLambdaQueryWrapperAVC);
        SgGovernSchemeHistoryDetailVO avcVo = new SgGovernSchemeHistoryDetailVO();
        BeanUtil.copyProperties(avcResult, avcVo);
        if (allVO.getBestSchemeId().equals(avcVo.getId())) {
            allVO.setSchemeType(GovernSchemeEnum.AVC.getCode());
        }
        copyHistoryVo(avcResult, avcVo);
        List<Double> governEffectAVC = new ArrayList<>();
        for (int i = 0; i < allVO.getPayBackPeriod(); i++) {
            governEffectAVC.add(allVO.getLoss() - avcVo.getLoss());
        }
        avcVo.setGovernEffectArray(governEffectAVC);
        allVO.setAvcData(avcVo);
        LambdaQueryWrapper<SgGovernSchemeParam> sgGovernSchemeParamLambdaQueryWrapperAVC = new LambdaQueryWrapper<>();
        sgGovernSchemeParamLambdaQueryWrapperAVC.eq(SgGovernSchemeParam::getId, avcResult.getSchemeParamId());
        SgGovernSchemeParam avcParamOne = sgGovernSchemeParamService.getOne(sgGovernSchemeParamLambdaQueryWrapperAVC);
        CommonSchemeParam avcParam = new CommonSchemeParam();
        BeanUtil.copyProperties(avcParamOne, avcParam);
        allVO.setAvc(avcParam);
        //处理快切的结果和参数
        LambdaQueryWrapper<SgGovernSchemeHistoryDetail> historyDetailLambdaQueryWrapperQuick = new LambdaQueryWrapper<>();
        historyDetailLambdaQueryWrapperQuick.eq(SgGovernSchemeHistoryDetail::getEvaluationBatch, sgGovernSchemeHistory.getEvaluationBatch())
                .eq(SgGovernSchemeHistoryDetail::getType, GovernSchemeEnum.QUICK.getCode());
        SgGovernSchemeHistoryDetail quickResult = sgGovernSchemeHistoryDetailService.getOne(historyDetailLambdaQueryWrapperQuick);
        SgGovernSchemeHistoryDetailVO quickVo = new SgGovernSchemeHistoryDetailVO();
        BeanUtil.copyProperties(quickResult, quickVo);
        copyHistoryVo(quickResult, quickVo);
        if (allVO.getBestSchemeId().equals(quickVo.getId())) {
            allVO.setSchemeType(GovernSchemeEnum.QUICK.getCode());
        }
        List<Double> governEffectQuick = new ArrayList<>();
        for (int i = 0; i < allVO.getPayBackPeriod(); i++) {
            governEffectQuick.add(allVO.getLoss() - quickVo.getLoss());
        }
        quickVo.setGovernEffectArray(governEffectQuick);
        allVO.setQuickData(quickVo);
        LambdaQueryWrapper<SgGovernSchemeParam> sgGovernSchemeParamLambdaQueryWrapperQuick = new LambdaQueryWrapper<>();
        sgGovernSchemeParamLambdaQueryWrapperQuick.eq(SgGovernSchemeParam::getId, quickResult.getSchemeParamId());
        SgGovernSchemeParam quickParamOne = sgGovernSchemeParamService.getOne(sgGovernSchemeParamLambdaQueryWrapperQuick);
        QuickSchemeParam quickParam = new QuickSchemeParam();
        BeanUtil.copyProperties(quickParamOne, quickParam);
        SgIncomingLine sgIncomingLine = incomingLineService.getById(quickParam.getIncomingLineId());
        if (Objects.nonNull(sgIncomingLine)) {
            quickParam.setIncomingLineId(sgIncomingLine.getName());
        }
        allVO.setQuick(quickParam);
        return allVO;
    }

    private void copyHistoryVo(SgGovernSchemeHistoryDetail upsResult, SgGovernSchemeHistoryDetailVO upsVo) {
        List<String> data = Arrays.asList(upsResult.getInvestYield().split(StrPool.UNDERLINE));
        List<Double> doubleList = data.stream()
                .map(Double::parseDouble)
                .collect(Collectors.toList());
        upsVo.setInvestYieldArray(doubleList);


        upsVo.setYearsArray(Arrays.asList(upsResult.getYears().split(StrPool.UNDERLINE)));


        List<String> data1 = Arrays.asList(upsResult.getInvestYear().split(StrPool.UNDERLINE));
        List<Double> doubleList1 = data1.stream()
                .map(Double::parseDouble)
                .collect(Collectors.toList());
        upsVo.setInvestYearArray(doubleList1);


        List<String> data2 = Arrays.asList(upsResult.getPayBack().split(StrPool.UNDERLINE));
        List<Double> doubleList2 = data2.stream()
                .map(Double::parseDouble)
                .collect(Collectors.toList());
        upsVo.setPayBackArray(doubleList2);

        List<String> data3 = Arrays.asList(upsResult.getCost().split(StrPool.UNDERLINE));
        List<Double> doubleList3 = data3.stream()
                .map(Double::parseDouble)
                .collect(Collectors.toList());
        upsVo.setCostArray(doubleList3);

    }

    /**
     * 从每个方案选一个作为最佳方案,顺便入库，方便日后查询历史数据
     *
     * @param sgGovernSchemeHistoryAllVO 方案数据
     */
    private void chooseBestScheme(SgGovernSchemeHistoryAllVO sgGovernSchemeHistoryAllVO) {
        SgGovernSchemeHistoryDetailVO upsData = sgGovernSchemeHistoryAllVO.getUpsData();
        upsData.setId(IdWorker.get32UUID());
        SgGovernSchemeHistoryDetailVO avcData = sgGovernSchemeHistoryAllVO.getAvcData();
        avcData.setId(IdWorker.get32UUID());
        SgGovernSchemeHistoryDetailVO quickData = sgGovernSchemeHistoryAllVO.getQuickData();
        quickData.setId(IdWorker.get32UUID());
        List<SgGovernSchemeHistoryDetailVO> tempList = new ArrayList<>();
        tempList.add(upsData);
        tempList.add(avcData);
        tempList.add(quickData);
        // 使用Stream API根据综合得分排序
        List<SgGovernSchemeHistoryDetailVO> sortedByResult = tempList.stream()
                .sorted(Comparator.comparingDouble(SgGovernSchemeHistoryDetailVO::getInvestmentResult).reversed())
                .collect(Collectors.toList());
        sgGovernSchemeHistoryAllVO.setBestSchemeId(sortedByResult.get(0).getId());
        //执行持久化操作
        SgGovernSchemeHistoryDetail upsDataPO = new SgGovernSchemeHistoryDetail();
        //拷贝数据到持久化对象中
        copyHistoryDetail(upsData, upsDataPO);
        sgGovernSchemeHistoryDetailService.save(upsDataPO);
        SgGovernSchemeHistoryDetail avcDataPO = new SgGovernSchemeHistoryDetail();
        copyHistoryDetail(avcData, avcDataPO);
        sgGovernSchemeHistoryDetailService.save(avcDataPO);
        SgGovernSchemeHistoryDetail quickDataPO = new SgGovernSchemeHistoryDetail();
        copyHistoryDetail(quickData, quickDataPO);
        sgGovernSchemeHistoryDetailService.save(quickDataPO);
        //将治理方案的历史数据存入库中
        SgGovernSchemeHistory sgGovernSchemeHistory = new SgGovernSchemeHistory();
        BeanUtil.copyProperties(sgGovernSchemeHistoryAllVO, sgGovernSchemeHistory);
        sgGovernSchemeHistoryService.save(sgGovernSchemeHistory);
    }

    /**
     * 拷贝数据到持久化对象中
     *
     * @param sourceData 源数据
     * @param targetData 持久化对象
     */
    private void copyHistoryDetail(SgGovernSchemeHistoryDetailVO sourceData, SgGovernSchemeHistoryDetail targetData) {
        BeanUtil.copyProperties(sourceData, targetData);
        targetData.setYears(String.join(StrPool.UNDERLINE, sourceData.getYearsArray()));

        String investYear = sourceData.getInvestYearArray().stream()
                .map(String::valueOf)
                .collect(Collectors.joining(StrPool.UNDERLINE));
        targetData.setInvestYear(investYear);


        String payBack = sourceData.getPayBackArray().stream()
                .map(String::valueOf)
                .collect(Collectors.joining(StrPool.UNDERLINE));
        targetData.setPayBack(payBack);


        String cost = sourceData.getCostArray().stream()
                .map(String::valueOf)
                .collect(Collectors.joining(StrPool.UNDERLINE));
        targetData.setCost(cost);

        String investYield = sourceData.getInvestYieldArray().stream()
                .map(String::valueOf)
                .collect(Collectors.joining(StrPool.UNDERLINE));
        targetData.setInvestYield(investYield);

    }


    /**
     * 将半成品梳理成合理的页面数据内容回显
     *
     * @param sgGovernSchemeHistoryAllVO 页面视图半成品
     */
    private void generateSchemeView(SgGovernSchemeHistoryAllVO sgGovernSchemeHistoryAllVO, ProductLineComputeData productLineComputeData) {
        //待治理的总功率
        List<SgMachineVO> machineVOList = productLineComputeData.getMachineVOList();
        double totalPower = machineVOList.stream().mapToDouble(SgMachineVO::getMachinePower).sum();
        sgGovernSchemeHistoryAllVO.setGovernPower(totalPower);
        //处理需要投资的信息
        //先处理UPS，即中电的串联
        calcEquipment(GovernSchemeEnum.UPS, sgGovernSchemeHistoryAllVO.getUps(), null, totalPower, sgGovernSchemeHistoryAllVO.getUpsData());
        calcEquipment(GovernSchemeEnum.AVC, sgGovernSchemeHistoryAllVO.getAvc(), null, totalPower, sgGovernSchemeHistoryAllVO.getAvcData());
        calcEquipment(GovernSchemeEnum.QUICK, null, sgGovernSchemeHistoryAllVO.getQuick(), totalPower, sgGovernSchemeHistoryAllVO.getQuickData());
        //处理雷达图
        //先处理UPS，即中电的串联
        calcRadar(sgGovernSchemeHistoryAllVO, GovernSchemeEnum.UPS);
        calcRadar(sgGovernSchemeHistoryAllVO, GovernSchemeEnum.AVC);
        calcRadar(sgGovernSchemeHistoryAllVO, GovernSchemeEnum.QUICK);
        //计算各个方案的优先指数
        calcPrecedence(sgGovernSchemeHistoryAllVO.getUpsData());
        calcPrecedence(sgGovernSchemeHistoryAllVO.getAvcData());
        calcPrecedence(sgGovernSchemeHistoryAllVO.getQuickData());


    }


    /**
     * 步骤拆解
     * 1、首先获取到生产线所有的设备数据
     * 2、循环时间范围内的电网接入水平
     * 3、再根据治理方案循环，计算出各个治理方案下的 特征幅值、持续时间的治理后的结果
     * 4、计算故障率
     * 4.1、计算设备的故障率，并计算出该设备的设备损失+原料损失
     * 4.2、计算生产线的故障率，计算出生产线的产能损失
     * <p>
     * 根据电网接入水平以及生产线的各敏感元器件，计算各方案下每年经济的损失
     *
     * @param eventList                  暂降事件列表
     * @param sgGovernSchemeHistoryAllVO 待返回前台展示数据
     * @param governSchemeEnum           治理方案
     * @param yearRate                   年化率
     */
    private void computeProductLineLoss(List<SgEvent> eventList,
                                        SgGovernSchemeHistoryAllVO sgGovernSchemeHistoryAllVO, GovernSchemeEnum governSchemeEnum,
                                        double yearRate, ProductLineComputeData productLineComputeData) {
        //为了数据好看点，除以年期望值
        Integer payBackPeriod = 1;
        //计算出来的方式：Integer payBackPeriod = sgGovernSchemeHistoryAllVO.getPayBackPeriod();
        SgGovernSchemeParam schemeParam;
        //总计暂降次数
        int eventCount = 0;
        //总计中断次数
        double breakCount = 0.0;
        //总计损失经济
        double totalLoss = 0.0;
        //特征幅值,持续时间
        double amplitude, durationTime;
        //计算每个频次再各方案下的累计损失
        switch (governSchemeEnum) {
            case UPS:
                for (SgEvent sgEvent : eventList) {
                    amplitude = calcAmplitude(sgGovernSchemeHistoryAllVO.getUps(), sgEvent.getFeatureAmplitude(), sgEvent.getDuration());
                    durationTime = calcDuration(sgGovernSchemeHistoryAllVO.getUps(), sgEvent.getDuration());
                    //得出当前该接入水平的经济损失以及生产线故障率
                    Double[] eventEffect = eventService.getEventLossAndFailureRate(productLineComputeData, amplitude, durationTime);
                    Double singleLoss = eventEffect[0];
                    Double productLineFailureRate = eventEffect[1];
                    //根据暂态区域分布计算：totalLoss = totalLoss + singleLoss * eventMap.get(sagData);
                    totalLoss = totalLoss + singleLoss;
                    if (amplitude < 0.9) {
                        eventCount++;
                        breakCount = breakCount + productLineFailureRate;
                    }
                }
                schemeParam = new SgGovernSchemeParam();
                schemeParam.setState(DataStateEnum.ENABLE.getCode());
                BeanUtil.copyProperties(sgGovernSchemeHistoryAllVO.getUps(), schemeParam);
                sgGovernSchemeParamService.save(schemeParam);
                SgGovernSchemeHistoryDetailVO upsGovernSchemeHistoryDetailVO = new SgGovernSchemeHistoryDetailVO();
                upsGovernSchemeHistoryDetailVO.setSchemeParamId(schemeParam.getId());
                upsGovernSchemeHistoryDetailVO.setEventCount(eventCount);
                upsGovernSchemeHistoryDetailVO.setBreakCount((int) breakCount);
                upsGovernSchemeHistoryDetailVO.setLoss(PubUtils.doubleRound(2, totalLoss));
                upsGovernSchemeHistoryDetailVO.setYearLoss(PubUtils.doubleRound(2, upsGovernSchemeHistoryDetailVO.getLoss() / payBackPeriod));
                upsGovernSchemeHistoryDetailVO.setEvaluationBatch(sgGovernSchemeHistoryAllVO.getEvaluationBatch());
                upsGovernSchemeHistoryDetailVO.setType(GovernSchemeEnum.UPS.getCode());
                sgGovernSchemeHistoryAllVO.setUpsData(upsGovernSchemeHistoryDetailVO);
                break;
            case AVC:
                for (SgEvent sgEvent : eventList) {
                    amplitude = calcAmplitude(sgGovernSchemeHistoryAllVO.getAvc(), sgEvent.getFeatureAmplitude(), sgEvent.getDuration());
                    durationTime = calcDuration(sgGovernSchemeHistoryAllVO.getAvc(), sgEvent.getDuration());
                    //得出当前该接入水平的经济损失以及生产线故障率
                    Double[] eventEffect = eventService.getEventLossAndFailureRate(productLineComputeData, amplitude, durationTime);
                    Double singleLoss = eventEffect[0];
                    Double productLineFailureRate = eventEffect[1];
                    //根据暂态区域分布计算出：totalLoss = totalLoss + singleLoss * eventMap.get(sagData);
                    totalLoss = totalLoss + singleLoss;
                    if (amplitude < 0.9) {
                        eventCount++;
                        breakCount = breakCount + productLineFailureRate;
                    }
                }
                schemeParam = new SgGovernSchemeParam();
                schemeParam.setState(DataStateEnum.ENABLE.getCode());
                BeanUtil.copyProperties(sgGovernSchemeHistoryAllVO.getAvc(), schemeParam);
                sgGovernSchemeParamService.save(schemeParam);
                SgGovernSchemeHistoryDetailVO avcGovernSchemeHistoryDetailVO = new SgGovernSchemeHistoryDetailVO();
                avcGovernSchemeHistoryDetailVO.setSchemeParamId(schemeParam.getId());
                avcGovernSchemeHistoryDetailVO.setEventCount(eventCount);
                avcGovernSchemeHistoryDetailVO.setBreakCount((int) breakCount);
                avcGovernSchemeHistoryDetailVO.setLoss(PubUtils.doubleRound(2, totalLoss));
                avcGovernSchemeHistoryDetailVO.setYearLoss(PubUtils.doubleRound(2, avcGovernSchemeHistoryDetailVO.getLoss() / payBackPeriod));
                avcGovernSchemeHistoryDetailVO.setEvaluationBatch(sgGovernSchemeHistoryAllVO.getEvaluationBatch());
                avcGovernSchemeHistoryDetailVO.setType(GovernSchemeEnum.AVC.getCode());
                sgGovernSchemeHistoryAllVO.setAvcData(avcGovernSchemeHistoryDetailVO);
                break;
            case AVC_RTS:
                //暂时无法知道如何计算串并联的治理方案
                break;
            default:
                for (SgEvent sgEvent : eventList) {
                    //不做治理
                    amplitude = sgEvent.getFeatureAmplitude();
                    durationTime = sgEvent.getDuration();
                    //得出当前该接入水平的经济损失以及生产线故障率
                    Double[] eventEffect = eventService.getEventLossAndFailureRate(productLineComputeData, amplitude, durationTime);
                    Double singleLoss = eventEffect[0];
                    Double productLineFailureRate = eventEffect[1];
                    totalLoss = totalLoss + singleLoss;
                    eventCount++;
                    breakCount = breakCount + productLineFailureRate;
                }
                if (governSchemeEnum.getCode() == 3) {
                    //快切治理的经济
                    SgGovernSchemeHistoryDetailVO quickSgGovernSchemeHistoryDetailVO = new SgGovernSchemeHistoryDetailVO();
                    quickSgGovernSchemeHistoryDetailVO.setEventCount(eventCount);
                    quickSgGovernSchemeHistoryDetailVO.setBreakCount((int) breakCount);
                    quickSgGovernSchemeHistoryDetailVO.setLoss(PubUtils.doubleRound(2, totalLoss));
                    quickSgGovernSchemeHistoryDetailVO.setYearLoss(PubUtils.doubleRound(2, quickSgGovernSchemeHistoryDetailVO.getLoss() / payBackPeriod));
                    quickSgGovernSchemeHistoryDetailVO.setEvaluationBatch(sgGovernSchemeHistoryAllVO.getEvaluationBatch());
                    quickSgGovernSchemeHistoryDetailVO.setType(GovernSchemeEnum.QUICK.getCode());
                    sgGovernSchemeHistoryAllVO.setQuickData(quickSgGovernSchemeHistoryDetailVO);
                } else {
                    //治理前的经济损失
                    sgGovernSchemeHistoryAllVO.setEventCount(eventCount);
                    sgGovernSchemeHistoryAllVO.setBreakCount((int) breakCount);
                    sgGovernSchemeHistoryAllVO.setLoss(totalLoss);
                    sgGovernSchemeHistoryAllVO.setYearLoss(PubUtils.doubleRound(2, sgGovernSchemeHistoryAllVO.getLoss() / payBackPeriod));
                }
                break;
        }
    }


    /**
     * 计算治理幅度
     *
     * @param schemeParam  治理参数
     * @param oriAmplitude 原始暂降降幅
     * @param oriDuration  原始持续时间
     * @return 新幅度
     */
    private double calcAmplitude(CommonSchemeParam schemeParam, double oriAmplitude, double oriDuration) {
        double newAmplitude = oriAmplitude;
        // 如果响应时间小于持续时间，并且后备时间大于持续时间，可治理，否则不做暂降治理
        if (schemeParam.getResponseTime() < oriDuration && schemeParam.getBackupTime() * 1000 > oriDuration) {
            newAmplitude = newAmplitude + schemeParam.getCompensation();
            if (newAmplitude > 1.0) {
                newAmplitude = 1.0;
            }
        }
        return newAmplitude;
    }


    /**
     * 计算治理时间
     *
     * @param schemeParam 治理参数
     * @param oriDuration 原始持续时间
     * @return 新幅度
     */
    private double calcDuration(CommonSchemeParam schemeParam, double oriDuration) {
        double newDuration = oriDuration;
        // 如果响应时间小于持续时间,就将持续时间需改为响应时间(后备时间暂不考虑)，否则不做持续时间治理
        if (schemeParam.getResponseTime() < oriDuration) {
            newDuration = schemeParam.getResponseTime();
        }
        return newDuration;
    }

    /**
     * @param sgGovernSchemeHistoryParam 页面参数
     * @return 初始化方案数据
     */
    private SgGovernSchemeHistoryAllVO initInvestmentScheme(SgGovernSchemeHistoryParam sgGovernSchemeHistoryParam) {
        SgGovernSchemeHistoryAllVO sgGovernSchemeHistoryAllVO = new SgGovernSchemeHistoryAllVO();
        BeanUtil.copyProperties(sgGovernSchemeHistoryParam, sgGovernSchemeHistoryAllVO);
        //提前获得一个id，作为该次评估所有方案的一个批次号
        String batchNo = IdWorker.get32UUID();
        sgGovernSchemeHistoryAllVO.setEvaluationBatch(batchNo);
        sgGovernSchemeHistoryAllVO.setTimeScope(sgGovernSchemeHistoryParam.getStartTime().concat("至").concat(sgGovernSchemeHistoryParam.getEndTime()));
        sgGovernSchemeHistoryAllVO.setQuick(sgGovernSchemeHistoryParam.getQuick());
        return sgGovernSchemeHistoryAllVO;
    }

    /**
     * 根据补偿方案和功率计算补偿设备
     *
     * @param governSchemeEnum  方案类型
     * @param commonSchemeParam 补偿标准
     * @param power             功率
     */
    private void calcEquipment(GovernSchemeEnum governSchemeEnum, CommonSchemeParam commonSchemeParam, QuickSchemeParam quickSchemeParam, Double power, SgGovernSchemeHistoryDetailVO resultVo) {
        // 避免NULL时报错
        resultVo.setCount(0);
        double priceIndex;
        switch (governSchemeEnum) {
            case UPS:
                resultVo.setSize("[2200*1100*2000]");
                resultVo.setCount((int) Math.ceil(power / 400));
                double totalPower;
                //根据新需求，总功率低于50kW时，按10kW一跳且价格上浮50%；高于50kW时每50kW一跳。
                if (power < 50) {
                    priceIndex = 1.5;
                    totalPower = 10 * Math.ceil(power / 10);
                } else {
                    priceIndex = 1;
                    totalPower = 50 * Math.ceil(power / 50);
                }
                resultVo.setPower(PubUtils.doubleRound(2, totalPower));
                resultVo.setEquipment(PubUtils.doubleRound(2, commonSchemeParam.getPrice() * resultVo.getPower() * priceIndex));
                resultVo.setMaintain(PubUtils.doubleRound(2, commonSchemeParam.getMaintainFee() * resultVo.getPower() * priceIndex));
                break;
            case AVC:
                AVCInfo avcInfo = calcAvc(power);
                resultVo.setSize(avcInfo.getSize());
                resultVo.setCount(avcInfo.getCount());
                resultVo.setPower(PubUtils.doubleRound(2, avcInfo.getTotalPower()));
                //根据新需求，总功率低于50kW时，按50kW一跳且价格上浮50%；高于50kW时每50kW一跳。
                priceIndex = 1;
                if (avcInfo.getTotalPower() < 50) {
                    priceIndex = 1.5;
                }
                resultVo.setEquipment(PubUtils.doubleRound(2, commonSchemeParam.getPrice() * resultVo.getPower() * priceIndex));
                resultVo.setMaintain(PubUtils.doubleRound(2, commonSchemeParam.getMaintainFee() * resultVo.getPower() * priceIndex));
                break;
            default:
                //默认为快切
                resultVo.setCount((int) Math.ceil(power / quickSchemeParam.getGovernMachinePower()));
                resultVo.setPower(PubUtils.doubleRound(2, quickSchemeParam.getGovernMachinePower() * resultVo.getCount()));
                resultVo.setSize("");
                resultVo.setEquipment(PubUtils.doubleRound(2, quickSchemeParam.getPrice() * resultVo.getCount() + quickSchemeParam.getBuildFee()));
                resultVo.setMaintain(PubUtils.doubleRound(2, quickSchemeParam.getQuickMaintainFee()));
                break;

        }
    }

    /**
     * 计算每个治理方案的治理效果，包括用户投资曲线、用户收益曲线、雷达图
     *
     * @param sgGovernSchemeHistoryAllVO 未治理前的一些数据
     * @param governSchemeEnum           指定治理方案枚举
     */
    private void calcRadar(SgGovernSchemeHistoryAllVO sgGovernSchemeHistoryAllVO, GovernSchemeEnum governSchemeEnum) {
        SgGovernSchemeHistoryDetailVO tempData;
        switch (governSchemeEnum) {
            case UPS:
                tempData = sgGovernSchemeHistoryAllVO.getUpsData();
                break;
            case AVC:
                tempData = sgGovernSchemeHistoryAllVO.getAvcData();
                break;
            default:
                tempData = sgGovernSchemeHistoryAllVO.getQuickData();
                break;
        }
        tempData.setRiskLevelBefore(RadarUtil.getRadarRiskLevel(sgGovernSchemeHistoryAllVO.getYearLoss(), 0.9));
        tempData.setRiskLevelAfter(RadarUtil.getRadarRiskLevel(tempData.getYearLoss(), 0.1));
        tempData.setRadarResult(RadarUtil.getRadarResult(sgGovernSchemeHistoryAllVO.getYearLoss(), tempData.getYearLoss()));
        tempData.setRadarRisk(RadarUtil.getRadarRisk(tempData.getYearLoss(), 0.1));
        //每年的投资效益,治理效果
        double earnings = sgGovernSchemeHistoryAllVO.getYearLoss() - tempData.getYearLoss();
        List<Double> investYear = new ArrayList<>();
        List<Double> payBack = new ArrayList<>();
        List<Double> investYearThree = new ArrayList<>();
        List<Double> payBackThree = new ArrayList<>();
        List<Double> cost = new ArrayList<>();
        List<Double> investYield = new ArrayList<>();
        //存在收益，计算下回收期，默认最大回收周期为最后一年
        int actualPayBack = sgGovernSchemeHistoryAllVO.getPayBackPeriod();
        boolean isFirst = true;
        //治理效果
        List<Double> governEffect = new ArrayList<>();
        for (int i = 1; i <= 15; i++) {
            //一直计算到用户最大期望回收周期
            double investMoney;
            if (i == 1) {
                //第一年的投资为设备费用+运维费
                investMoney = tempData.getEquipment() + tempData.getMaintain();
            } else {
                //每年的投资为运维费
                investMoney = tempData.getMaintain();
            }
            investMoney = PubUtils.doubleRound(2, investMoney);
            //需要计算额外的5 10 15年需要的数据
            if (i <= sgGovernSchemeHistoryAllVO.getPayBackPeriod()) {
                investYear.add(investMoney);
                investYearThree.add(investMoney);
                double totalInvest = investYear.stream().mapToDouble(Double::doubleValue).sum();
                payBack.add(PubUtils.doubleRound(2, earnings * i - totalInvest));
                payBackThree.add(earnings * i - totalInvest);
                if (earnings * i - totalInvest > 0) {
                    if (isFirst) {
                        //仅第一次给投资期赋值
                        actualPayBack = i;
                        isFirst = false;
                    }
                }
                governEffect.add(earnings);
            } else {
                //如果超出预期日期，还需要循环15次，把数据添加进来，用于计算5 10 15的数据
                investYearThree.add(investMoney);
                double totalInvest = investYear.stream().mapToDouble(Double::doubleValue).sum();
                payBackThree.add(earnings * i - totalInvest);
            }
        }

        //计算 5 10 15的数据
        for (int i = 5; i <= 15; i += 5) {
            List<Double> tempInvest = investYearThree.subList(0, i);
            double sum = tempInvest.stream().mapToDouble(Double::doubleValue).sum();
            cost.add(PubUtils.doubleRound(2, sum));

            List<Double> tempPayBack = payBackThree.subList(0, i);
            double sumPayBack = tempPayBack.stream().mapToDouble(Double::doubleValue).sum();
            double yield = sumPayBack / sum * 100;
            investYield.add(PubUtils.doubleRound(2, yield));
        }
        tempData.setGovernEffectArray(governEffect);
        tempData.setCostArray(cost);
        tempData.setInvestYieldArray(investYield);

        if (earnings > 0) {
            double wish = (double) (sgGovernSchemeHistoryAllVO.getPayBackPeriod() - actualPayBack) / sgGovernSchemeHistoryAllVO.getPayBackPeriod();
            tempData.setRadarWish(PubUtils.doubleRound(2, wish));
        } else {
            //没有收益，则投资意愿自然为0
            tempData.setRadarWish(0D);
        }
        //每年投资
        tempData.setInvestYearArray(investYear);
        //每年收益
        tempData.setPayBackArray(payBack);
        //实际回报年份
        tempData.setActualPayBack(actualPayBack);
        //年份
        List<String> years = new ArrayList<>();
        //从当前年份作为起始年份，截止年份采用期望回收周期
        int year = LocalDate.now().getYear();
        for (int i = 0; i < sgGovernSchemeHistoryAllVO.getPayBackPeriod(); i++) {
            years.add(String.valueOf(year + i));
        }
        tempData.setYearsArray(years);
        double sagImmunity = (double) (sgGovernSchemeHistoryAllVO.getEventCount() - tempData.getEventCount()) / sgGovernSchemeHistoryAllVO.getEventCount();
        tempData.setSagImmunity(PubUtils.doubleRound(2, sagImmunity));
    }


    /**
     * 计算各方案下优先指数
     *
     * @param governSchemeHistoryDetailVO 投资方案下的所有数据，根据雷达信息计算出优先指数
     */
    private void calcPrecedence(SgGovernSchemeHistoryDetailVO governSchemeHistoryDetailVO) {
        //去风险能力
        double radarRiskFactor = 0.1070;
        //投资意愿
        double radarWishFactor = 0.2538;
        //暂降免疫能力
        double radarRmmunityFactor = 0.0443;
        //治理效果
        double radarResultFactor = 0.5949;
        //方案优先指数
        double optimizingResult = governSchemeHistoryDetailVO.getRadarResult() * radarResultFactor
                + governSchemeHistoryDetailVO.getRadarWish() * radarWishFactor
                + governSchemeHistoryDetailVO.getRadarRisk() * radarRiskFactor
                + governSchemeHistoryDetailVO.getSagImmunity() * radarRmmunityFactor;
        governSchemeHistoryDetailVO.setInvestmentResult(PubUtils.doubleRound(4, optimizingResult));
    }


    /**
     * 根据总功率计算需要的设备
     *
     * @param power 总功率
     */
    private AVCInfo calcAvc(Double power) {
        List<AVCInfo> dvrParamList = new ArrayList<>();
        dvrParamList.add(new AVCInfo(300, "[1600*1000*2100]", power));
        dvrParamList.add(new AVCInfo(600, "[3200*1100*2100]", power));
        dvrParamList.add(new AVCInfo(900, "[4800*1100*2100]", power));
        dvrParamList.add(new AVCInfo(1200, "[6400*1100*2100]", power));
        dvrParamList.add(new AVCInfo(1500, "[8000*1100*2100]", power));
        dvrParamList.add(new AVCInfo(1800, "[8200*1100*2100]", power));
        dvrParamList.add(new AVCInfo(2100, "[8200*1100*2100]", power));
        dvrParamList.add(new AVCInfo(2400, "[8200*1100*2100]", power));
        Collections.sort(dvrParamList);
        return dvrParamList.get(0);
    }

    @Setter
    @Getter
    static class AVCInfo implements Comparable<AVCInfo> {
        private int totalPower;
        private int power;
        private int count;
        private String size;

        public AVCInfo(int power, String size, double totalPower) {
            this.power = power;
            this.count = (int) Math.ceil(totalPower / this.power);
            this.size = size;
            // 此处后续被覆盖了，this.totalPower = this.power * this.count;
            //根据新需求，总功率低于50kW时，按50kW一跳且价格上浮50%；高于50kW时每50kW一跳。
            double power4price;
            if (totalPower >= 50) {
                power4price = 50;
            } else {
                power4price = 10;
            }
            this.totalPower = (int) (power4price * (Math.ceil(totalPower / power4price)));
        }

        @Override
        public int compareTo(AVCInfo o) {
            int a = this.totalPower - o.getTotalPower();
            if (a == 0) {
                return this.getCount() - o.getCount();
            } else {
                return a;
            }
        }
    }

}
