package com.jiayue.ipfcst.console.service;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.jiayue.ipfcst.common.data.entity.*;
import com.jiayue.ipfcst.common.data.repository.AccuracyPassRateRepository;
import com.jiayue.ipfcst.common.data.repository.ForecastPowerShortTermHisRepository;
import com.jiayue.ipfcst.common.data.repository.ForecastPowerUltraShortTermHisRepository;
import com.jiayue.ipfcst.common.data.repository.PowerStationStatusDataRepository;
import com.jiayue.ipfcst.common.data.service.BaseService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 西南准确率
 *
 * @author yh
 * @version 2.0
 * @since 2019/7/17 9:30
 */
@Service
@Slf4j
public class AccuracyPassRateCalculateForXNService extends BaseService {

  private final PowerStationStatusDataRepository powerStationStatusDataRepository;
  private final ForecastPowerUltraShortTermHisRepository forecastPowerUltraShortTermHisRepository;
  private final ForecastPowerShortTermHisRepository forecastPowerShortTermHisRepository;
  private final SysParameterService sysParameterService;
  private final AccuracyPassRateRepository accuracyPassRateRepository;

  @Autowired
  public AccuracyPassRateCalculateForXNService(PowerStationStatusDataRepository powerStationStatusDataRepository,
                                               ForecastPowerUltraShortTermHisRepository forecastPowerUltraShortTermHisRepository,
                                               ForecastPowerShortTermHisRepository forecastPowerShortTermHisRepository,
                                               SysParameterService sysParameterService,
                                               AccuracyPassRateRepository accuracyPassRateRepository) {
    this.powerStationStatusDataRepository = powerStationStatusDataRepository;
    this.forecastPowerUltraShortTermHisRepository = forecastPowerUltraShortTermHisRepository;
    this.forecastPowerShortTermHisRepository = forecastPowerShortTermHisRepository;
    this.sysParameterService = sysParameterService;
    this.accuracyPassRateRepository = accuracyPassRateRepository;
  }

  public void generateData(ElectricField electricField) {
    try{

      long moment15 = 900000L;
      //获取昨天 00:15
      long strartTime = DateUtil.offsetDay(DateUtil.beginOfDay(DateTime.now()), -1).getTime()+moment15;
      //获取今日凌晨00:00
      long endTime = DateUtil.beginOfDay(DateTime.now()).getTime();

        //获取实际功率
        List<PowerStationStatusData> rList = new ArrayList<>();
        //15分钟间隔实际功率
        List<PowerStationStatusData> rpList = new ArrayList<>();
        //取实际功率瞬时值，因为要过滤15分钟间隔实际功率集合，所以要获取时间从00:00开始 这样才能获取到00:15的数据（>=00:00,<00:00）
        rList = powerStationStatusDataRepository.findByTimeBetween(new Date(strartTime), new Date(endTime));
        rList.sort(Comparator.comparing(PowerStationStatusData::getTime));
        //过滤出15分钟间隔的实际功率集合
        for (long i = strartTime; i <= endTime; i = i + moment15) {
          long finalI = i;
          List<PowerStationStatusData> rpResultList = new ArrayList<>();

          rpResultList = rList.stream().filter(t -> t.getTime().getTime() == finalI ).collect(Collectors.toList());

          if (rpResultList != null && rpResultList.size() > 0) {
            rpList.add(rpResultList.get(0));
          }
        }

        //短期准确率
        Float dq = null;
        //超短期准确率
        Float cdq = null;

        if (rpList != null && rpList.size() > 0) {
          /*************************************************计算短期准确率-start*****************************************************/
          List<ForecastPowerShortTermHis> stList = new ArrayList<>();
          List<Map<String, BigDecimal>> stResultList = new ArrayList<>();
          // 获取短期计算准确率获取该天是提前几天预测的
          String sysParameter = sysParameterService.getSysParameter("ACCURACY_DQ_HOWLONGAGO","1");
          int howLongAgoForDq = Integer.parseInt(sysParameter);
          stList = forecastPowerShortTermHisRepository.findByForecastTimeBetweenAndForecastHowLongAgo(strartTime, endTime, howLongAgoForDq);
          stResultList = this.dataFilter(stList, rpList, electricField.getElectricFieldTypeEnum().name(), "dq", strartTime, endTime);
          dq = this.calculationsData(stResultList,electricField.getCapacity());
          /*************************************************计算短期准确率-end*****************************************************/

          /*************************************************计算超短期准确率-start*****************************************************/
          List<ForecastPowerUltraShortTermHis> ustList = new ArrayList<>();
          List<Map<String, BigDecimal>> ustResultList = new ArrayList<>();
          String sysParameterCdq = sysParameterService.getSysParameter("ACCURACY_CDQ_HOWLONGAGO","1");
          int howLongAgoForCdq = Integer.parseInt(sysParameter);
          ustList = forecastPowerUltraShortTermHisRepository.findByForecastTimeBetweenAndForecastHowLongAgo(strartTime, endTime,howLongAgoForCdq);
          ustResultList = this.dataFilter(ustList, rpList, electricField.getElectricFieldTypeEnum().name(), "cdq", strartTime, endTime);
          cdq = this.calculationsData(ustResultList,electricField.getCapacity());
          /*************************************************计算超短期准确率-end*****************************************************/

        }

        //入库
      AccuracyPassRate accuracyPassRate = new AccuracyPassRate();
      accuracyPassRate.setTime(DateUtil.offsetDay(DateUtil.beginOfDay(DateTime.now()), -1).getTime());
      if(dq == null){
        accuracyPassRate.setShortTermAccuracy("缺失数据,无法计算");
      }else{
        accuracyPassRate.setShortTermAccuracy(dq.toString());
      }
      if(cdq == null){
        accuracyPassRate.setUltraShortTermAccuracy("缺失数据,无法计算");
      }else{
        accuracyPassRate.setUltraShortTermAccuracy(cdq.toString());
      }

      accuracyPassRateRepository.save(accuracyPassRate);


    }catch (Exception e) {
      e.printStackTrace();
      log.error("西南计算短期和超短期准确率失败" + e);
    }

  }

  /**
   * 数据过滤
   *
   * @param list              需要过滤的集合
   * @param electricFieldType 场站类型
   * @param dataType          数据类型
   * @param rList             实际功率结果集
   * @param strartTime        开始时间
   * @param endTime           结束时间
   * @return 过滤后的结果集
   */
  private List<Map<String, BigDecimal>> dataFilter(List list, List<PowerStationStatusData> rList, String electricFieldType, String dataType, long strartTime, long endTime) {


    List<ForecastPowerShortTermHis> dqList = new ArrayList<>();
    List<ForecastPowerUltraShortTermHis> cdqList = new ArrayList<>();
    List<Map<String, BigDecimal>> result = new ArrayList<>();
    if (dataType.equals("dq")) {
      dqList = list;
    } else {
      cdqList = list;
    }
    //只要有一个集合为空，那么准确率就无法计算
    if ((rList != null && rList.size() > 0) && (list != null && list.size() > 0)) {
      for (long i = strartTime; i <= endTime; i = i + 1000 * 60 * 15L) {
        long stepTime = i;
        //过滤出该时间的预测值
        List ycFilter = null;
        if (dataType.equals("dq")) {
          ycFilter = dqList.stream().filter(t -> t.getForecastTime() == stepTime).collect(Collectors.toList());
        } else {
          ycFilter = cdqList.stream().filter(t -> t.getForecastTime() == stepTime).collect(Collectors.toList());
        }

        //过滤出该时间的实际功率值
        List<PowerStationStatusData> rpFilter = null;
        rpFilter = rList.stream().filter(t -> t.getTime().getTime() == stepTime).collect(Collectors.toList());
        //过滤掉同一时刻预测和实际值有缺失的数据
        if ((ycFilter != null && ycFilter.size() > 0) && (rpFilter != null && rpFilter.size() > 0)) {
          //实际值
          BigDecimal rpValue = rpFilter.get(0).getAbleValue();
          //预测值
          BigDecimal ycValue;
          if (dataType.equals("dq")) {
            ycValue = ((List<ForecastPowerShortTermHis>) ycFilter).get(0).getAbleValue();
          } else {
            ycValue = ((List<ForecastPowerUltraShortTermHis>) ycFilter).get(0).getAbleValue();
          }
          //区分光伏场站和风电场站
          if (electricFieldType.equals("E1")) {
            //光场站目前未知，先当无过滤条件处理
            Map<String, BigDecimal> map = new HashMap<>();
            map.put("yc", ycValue);
            map.put("rp", rpValue);
            result.add(map);
          } else {
            //风场站无过滤条件
            Map<String, BigDecimal> map = new HashMap<>();
            map.put("yc", ycValue);
            map.put("rp", rpValue);
            result.add(map);
          }


        }

      }
    }

    return result;
  }

  /**
   * 计算准确率
   *
   * @param queryData 预测值和实际值的集合
   * @return 准确率
   */
  private Float calculationsData(List<Map<String, BigDecimal>> queryData,BigDecimal cap) throws Exception {
    Float result = null;

    if (queryData != null && queryData.size() > 0) {
      //总和
      BigDecimal sum = new BigDecimal(0);

      for (Map<String, BigDecimal> map : queryData) {
        //预测值
        BigDecimal yc = map.get("yc").setScale(3, BigDecimal.ROUND_HALF_UP);
        //实际值
        BigDecimal rp = map.get("rp").setScale(4, BigDecimal.ROUND_HALF_UP);

        //DifferenceValue = 实际值-预测值
        BigDecimal DifferenceValue = rp.subtract(yc).setScale(4, BigDecimal.ROUND_HALF_UP);
        //square = DifferenceValue*DifferenceValue
        BigDecimal square = DifferenceValue.multiply(DifferenceValue).setScale(9, BigDecimal.ROUND_HALF_UP);
        sum = sum.add(square);
      }
      BigDecimal number = new BigDecimal(1);

      //样本总数
      BigDecimal totalValue = new BigDecimal(queryData.size());
      //√n: n为样本总数
      double totalSqrt = Math.sqrt(totalValue.doubleValue());

      BigDecimal totalSqrtBig = new BigDecimal(totalSqrt);
      // 装机总量
      if(cap.floatValue() ==0f){
        cap = new BigDecimal("1");
        // 进行告警
        log.warn("计算准确率装机容量为0，修改为1");
      }

      //cap*√n
      BigDecimal multiplyValue = totalSqrtBig.multiply(cap).setScale(9, BigDecimal.ROUND_HALF_UP);

      //√sum
      double sumSqrt =Math.sqrt(sum.doubleValue());

      BigDecimal sumSqrtBig = new BigDecimal(sumSqrt);

      BigDecimal divideValue = sumSqrtBig.divide(multiplyValue, 9, BigDecimal.ROUND_HALF_UP);
      //1-sqrtValue
      BigDecimal subtractValue = number.subtract(divideValue).setScale(9, BigDecimal.ROUND_HALF_UP);
      //*100%
      BigDecimal resultBig = subtractValue.multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP);

      result = resultBig.floatValue();

    }

    return result;
  }



}
