package com.jiayue.ipfcst.service;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.jiayue.ipfcst.common.core.exception.BusinessException;
import com.jiayue.ipfcst.common.core.util.DateMomentUtil;
import com.jiayue.ipfcst.common.data.constant.enums.FileStatusEnum;
import com.jiayue.ipfcst.common.data.constant.enums.FileTypeEnum;
import com.jiayue.ipfcst.common.data.entity.*;
import com.jiayue.ipfcst.common.data.repository.*;
import com.jiayue.ipfcst.common.data.service.BaseService;
import com.jiayue.ipfcst.common.data.service.uploadfilerule.IUploadFileRuleService;
import com.sun.istack.internal.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.quartz.CronExpression;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 上报计算业务层
 *
 * @author yh
 * @version 1.0
 * @since 2019/8/5 16:02
 */
@Service
@Slf4j
public class UploadFileCountService extends BaseService {
  private final UploadObjectRepository uploadObjectRepository;
  private final UploadFileLogRepository uploadFileLogRepository;
  private final HolidayCalendarRepository holidayCalendarRepository;
  private final QuartzRepository quartzRepository;
  @Autowired
  private Map<String, IUploadFileRuleService> uploadFileRuleServiceMap;

  public UploadFileCountService(UploadObjectRepository uploadObjectRepository,
                                UploadFileLogRepository uploadFileLogRepository,
                                HolidayCalendarRepository holidayCalendarRepository,
                                  QuartzRepository quartzRepository) {
    this.uploadObjectRepository = uploadObjectRepository;
    this.uploadFileLogRepository = uploadFileLogRepository;
    this.holidayCalendarRepository = holidayCalendarRepository;
    this.quartzRepository = quartzRepository;
  }

  /**
   * 上报率计算
   * @param queryTime 查询时间
   * @return 结果集
   * @throws BusinessException 业务异常
   */

  public Map<String,Object> reportingRateCount(Long queryTime) throws BusinessException {
    Map<String,Object> resultMap = new HashMap<>();
    Map<String,String> queryTimeMap = new HashMap<>();
    //开始时间
    Long startTime = DateUtil.beginOfDay(new Date(queryTime)).getTime();
    Long endTime = DateTime.now().getTime();
    //计算系统时间和查询时间的相差天数
    long distanceDay = DateUtil.betweenDay(new Date(startTime), DateTime.now(), true);
    if(distanceDay != 0){
      //查询时间不是今天 结束时间取查询当天23:59:59
      endTime = DateUtil.offsetDay(DateUtil.beginOfDay(new Date(queryTime)), 1).getTime()-1000L;
    }
    queryTimeMap.put("startTime",DateUtil.format(new Date(startTime),"yyyy-MM-dd HH:mm:ss"));
    queryTimeMap.put("endTime",DateUtil.format(new Date(endTime),"yyyy-MM-dd HH:mm:ss"));

    List<Map<String,Object>> result = new ArrayList<>();
    //获取所有上报对象
    List<UploadObject> allUploadObject = uploadObjectRepository.findAll();
    List<Quartz> quartzList = quartzRepository.findAll();
    List<UploadObject> allUploadObjectSort = allUploadObject.stream().sorted((a, b) -> a.getId() - b.getId()).collect(Collectors.toList());

    if (uploadFileRuleServiceMap != null) {
      String pro = "";
      ElectricField electricField = null;
      try {
        electricField = super.getElectricField();
      } catch (BusinessException e) {
        e.printStackTrace();
      }
      if (electricField != null) {
        pro = electricField.getProvinceEnum().name();
      }
      String demo = super.getSysParameter("demo","1");
      IUploadFileRuleService uploadFileRuleService = uploadFileRuleServiceMap.get(pro.toLowerCase() + "UploadFileRuleService");
      if (uploadFileRuleService instanceof IUploadFileRuleService) {
        for (UploadObject u : allUploadObjectSort) {
          // 获取上报对象上报类型
          String[] fileTypeArray = u.getUploadFileType().split(",");
          for (String fileType : fileTypeArray) {
            String fileTypeName = FileTypeEnum.valueOf(fileType).getMessage();
            Map<String,Object> map  = new HashMap<>();
            //实际上报数
            BigDecimal realityNum = new BigDecimal("0");
            String executeClass = "com.jiayue.ipfcst.fileupload.job.UploadFile"+fileType+"Job";
            List<Quartz> quartzs = quartzList.stream().filter(q -> executeClass.equals(q.getExecuteClass())).collect(Collectors.toList());
            String cronStr = "";
            if (quartzs.size()>0){
              cronStr = quartzs.get(0).getCronExpression();
            }

            if (fileType.equals("E156")||fileType.equals("E154")||fileType.equals("E158")){
              // 未来12个月预测发电量
              CronExpression cronExpression = null;
              try {
                cronExpression = new CronExpression(cronStr);
              } catch (ParseException e) {
                e.printStackTrace();
              }
              // cron表达式日期
              String str = cronExpression.getExpressionSummary();
              String tempCron = "{"+str.replaceAll("\n",",")+"}";
              JSONObject json = JSONUtil.parseObj(tempCron);
              String cronDaysOfMonth = json.get("daysOfMonth").toString();

              // 参数日期
              Calendar calendar = Calendar.getInstance();
              calendar.setTime(new Date(queryTime));
              int paramDayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
              if (paramDayOfMonth!=Integer.parseInt(cronDaysOfMonth)){
                continue;
              }
            }

            List<String> shoudFileNameList = uploadFileRuleService.getShouldFile(electricField, new Date(queryTime), fileType,cronStr);

            /*//理论上报数
            BigDecimal theoryNum = getFileNum(fileType,startTime,endTime);*/
            BigDecimal theoryNum = new BigDecimal(shoudFileNameList.size());

            BigDecimal rate = new BigDecimal("0");
            //获取实际上报数
            List<UploadFileLog> uploadFileLogList = uploadFileLogRepository.findByUploadObjectIdAndFileTypeEnumAndFileStatusEnumAndCreateTimeBetween(u.getId(), FileTypeEnum.valueOf(fileType), FileStatusEnum.valueOf("E2"),new Date(startTime), new Date(endTime));
            List<UploadFileLog> uploadFileLog = uploadFileLogList.stream().distinct().collect(Collectors.toList());

            realityNum = new BigDecimal(uploadFileLog.size());

            if (theoryNum.compareTo(new BigDecimal(0))==0){
              map.put("theoryNum","未到时间");
              map.put("rate","未到时间");
            }
            else{
              rate = realityNum.divide(theoryNum, 2, BigDecimal.ROUND_UP);
              map.put("theoryNum",theoryNum);
              if ("1".equals(demo)){
                map.put("rate","100%");
                map.put("realityNum",theoryNum);
              }
              else{
                map.put("rate",String.valueOf(rate.multiply(new BigDecimal("100")).floatValue())+"%");
                map.put("realityNum",realityNum);
              }
            }

            map.put("uploadObjectName",u.getUploadObjectName());
            map.put("fileType",fileTypeName);


            result.add(map);
          }


        }
      }else{
        log.warn("无该地区上报规则类");
      }


    }


    resultMap.put("data",result);
    resultMap.put("queryTime",queryTimeMap);
    return resultMap;

  }



  /**
   * 获取截止当前时间文件总个数
   *
   * @param fileType 文件类型
   * @param startTime 开始时间
   * @param endTime 结束时间
   * @return 文件总个数
   */
  public BigDecimal getFileNum(@NotNull final String fileType,Long startTime,Long endTime) throws BusinessException {
    String provinceName = getElectricField().getProvinceEnum().name();
    BigDecimal moment15 = new BigDecimal("900000");
    BigDecimal moment5 = new BigDecimal("300000");
    BigDecimal moment1 = new BigDecimal("60000");
    BigDecimal only = new BigDecimal("1");
    BigDecimal zero = new BigDecimal("0");

    switch (fileType){
      case "E1":
        // 短期
        if(provinceName.equals("E13")||provinceName.equals("E62")){
          //河北、甘肃  节假日N+1个文件
          return new BigDecimal(calculateDQDataDays(startTime));
        }else if(provinceName.equals("E21")){
          //辽宁获取参数
          String sysParameter = super.getSysParameter("FILE_FORECAST_DAYS", "3");
          return new BigDecimal(sysParameter);
        }else{
          return only;
        }

      case "E2":
        //超短期
        return this.calculateFileNum(moment15,startTime,endTime);

      case "E3":
        // 理论功率
        if(provinceName.equals("E43")){
          // 湖南 1分钟/次
          return this.calculateFileNum(moment1,startTime,endTime);
        }else{
          return this.calculateFileNum(moment5,startTime,endTime);
        }

      case "E4":
        // 24小时数值天气预报
        return only;

      case "E5":
        // 72小时数值天气预报
        return only;
      case "E6":
        // 气象站
        if(provinceName.equals("E22")){
          // 吉林 15分钟/次
          return this.calculateFileNum(moment15,startTime,endTime);
        }else if(provinceName.equals("E43")){
          // 湖南 1分钟/次
          return this.calculateFileNum(moment1,startTime,endTime);
        }else{
          return this.calculateFileNum(moment5,startTime,endTime);
        }
      case "E7":
        //测风塔
        if(provinceName.equals("E13")){
          return only;
        }else{
          return this.calculateFileNum(moment5,startTime,endTime);
        }

      case "E8":
        //逆变器
        if(provinceName.equals("E43")){
          // 湖南 1分钟/次
          return this.calculateFileNum(moment1,startTime,endTime);
        }else{
          return this.calculateFileNum(moment5,startTime,endTime);
        }
      case "E9":
        //风机
        if(provinceName.equals("E23")||provinceName.equals("E63")){
          // 黑龙江 青海 5分钟/次
          return this.calculateFileNum(moment5,startTime,endTime);
        }else{
          return only;
        }
      case "E10":
        //光伏组件
        return only;
      case "E11":
        //检修计划
        if(provinceName.equals("E43")){
          // 湖南 15分钟/次
          return this.calculateFileNum(moment15,startTime,endTime);
        }else{
          return only;
        }
      case "E12":
        //昨日开机容量
        return only;
      case "E13":
        //短期发电计划
        if(provinceName.equals("E62")){
          // 甘肃 n+1
          return new BigDecimal(calculateJHDataDays(startTime));
        }else{
          return only;
        }
      case "E14":
        //超短期发点计划
        return this.calculateFileNum(moment15,startTime,endTime);
      case "E15":
        //日前预测
        return only;
      case "E16":
        //日前预测
        return only;
      case "E17":
        //短期168
        return only;
      case "E18":
        //限电计划
        return only;
      case "E19":
        //弃电计划
        return only;
      case "E20":
        //场站信息
        return new BigDecimal(0);
      case "E21":
        //短期_集控
        return only;
      case "E22":
        //超短期_集控
        return this.calculateFileNum(moment15,startTime,endTime);
      case "E23":
        //测风塔_集控
        return this.calculateFileNum(moment5,startTime,endTime);
      case "E24":
        //72NWP_集控
        return only;
      case "E25":
        //短期_D5000
        String sysParameter = super.getSysParameter("FILE_FORECAST_DAYS", "3");
        return new BigDecimal(sysParameter);
      case "E26":
        //可用短期
        return only;
      case "E27":
        //可用超短期
        return this.calculateFileNum(moment15,startTime,endTime);
      case "E28":
        //理论超短期
        return this.calculateFileNum(moment15,startTime,endTime);
      case "E29":
        //预测风速风向
        return only;
      case "E30":
        //实测功率
        return only;
      case "E31":
        //预测辐照度
        return only;
      case "E32":
        //短期日志
        return only;
      case "E33":
        //超短期日志
        return this.calculateFileNum(moment15,startTime,endTime);
      case "E34":
        //短期可用日志
        return only;
      case "E35":
        //超短期可用日志
        return this.calculateFileNum(moment15,startTime,endTime);
      case "E36":
        //气象站日志
        return this.calculateFileNum(moment5,startTime,endTime);
      case "E37":
        //理论功率日志
        return this.calculateFileNum(moment5,startTime,endTime);
      case "E38":
        //逆变器日志
        return this.calculateFileNum(moment5,startTime,endTime);
      case "E39":
        //风机信息日志
        return this.calculateFileNum(moment5,startTime,endTime);
      case "E40":
        //测风塔日志
        return this.calculateFileNum(moment5,startTime,endTime);
      case "E41":
        //指标数据
        return only;
      case "E44":
        //升压变
        return this.calculateFileNum(moment5,startTime,endTime);
      case "E46":
        //超短期SFTP
        return this.calculateFileNum(moment15,startTime,endTime);
      case "E48":
        //单机
        return this.calculateFileNum(moment5,startTime,endTime);
      case "E62":
        //湖北旧短期
        return only;
      case "E63":
        //湖北旧超短期
        return this.calculateFileNum(moment15,startTime,endTime);
      case "E64":
        //湖北旧72nwp
        return only;

      case "E50":
        //海南升压站信息
        return this.calculateFileNum(moment15,startTime,endTime);
      case "E51":
        //海南AGC_AVC
        return this.calculateFileNum(moment15,startTime,endTime);
      case "E52":
        //海南风电总体信息
        return this.calculateFileNum(moment15,startTime,endTime);
      case "E53":
        //海南光伏总体信息
        return this.calculateFileNum(moment15,startTime,endTime);
      case "E54":
        //海南统计信息
        return this.calculateFileNum(moment15,startTime,endTime);
      case "E55":
        //海南逆变器/汇流箱信息
        return this.calculateFileNum(moment15,startTime,endTime);
      case "E56":
        //海南海南短期预测
        return only;
      case "E57":
        //海南海南超短期预测
        return this.calculateFileNum(moment15,startTime,endTime);
      case "E76":
        //海南海南风电机组
        return this.calculateFileNum(moment15,startTime,endTime);
      case "E83":
        //海南压缩包
        return this.calculateFileNum(moment15,startTime,endTime);


      case "E65":
        //湖北旧气象站
        return this.calculateFileNum(moment5,startTime,endTime);
      case "E66":
        //湖北旧测风塔
        return this.calculateFileNum(moment5,startTime,endTime);
      case "E67":
        //湖北旧单机
        return this.calculateFileNum(moment5,startTime,endTime);
      case "E68":
        //湖北地调短期
        return only;
      case "E69":
        //湖北地调超短期
        return this.calculateFileNum(moment15,startTime,endTime);
      case "E70":
        //湖北地调72nwp
        return only;
      case "E71":
        //湖北地调气象站
        return this.calculateFileNum(moment5,startTime,endTime);
      case "E72":
        //湖北地调测风塔
        return this.calculateFileNum(moment5,startTime,endTime);
      case "E73":
        //湖北地调单机
        return this.calculateFileNum(moment5,startTime,endTime);
      case "E74":
        //湖北地调风机信息
        return only;
      case "E75":
        //湖北地调逆变器
        return only;
      default:
        return only;


    }
  }

  /**
   * 获取短期N+1数据天数
   * @param todayTime 开始时间
   * @return 查询数据的天数
   */
  public int calculateDQDataDays(Long todayTime) {
    /*long todayTime = DateUtil.beginOfDay(new Date()).getTime();*/
    // 获取日历中所有记录
    List<HolidayCalendar> holidayCalendarList = holidayCalendarRepository.findAll();
    // 今日是否假期，false为工作日。
    boolean currentDayIsHoliday = false;
    // 第二天是否假期，false为工作日。
    boolean secondDayIsHoliday = false;
    // 计算出第二天的日期
    Long secondDay = DateUtil.offset(DateUtil.beginOfDay(new Date(todayTime)), DateField.DAY_OF_MONTH, 1).getTime();

    for (HolidayCalendar holidayCalendar : holidayCalendarList) {
      Long startTime = holidayCalendar.getStartTime();
      Long endTime = holidayCalendar.getEndTime();
      //类型 1假期 2特殊工作日【只可以是礼拜六和礼拜日】
      int type = holidayCalendar.getHolidayTypeEnum().getCode();
      if (startTime <= todayTime && endTime >= todayTime && type == 1) {
        // 今日是假期
        currentDayIsHoliday = true;
      }
      if (startTime <= secondDay && endTime >= secondDay && type == 1) {
        // 第二天是假期
        secondDayIsHoliday = true;
      }
    }
    // 定义数据天数变量
    int dataDays = 0;
    if (!currentDayIsHoliday && secondDayIsHoliday) {
      // 今日是工作日并且第二天开始是假期，遍历第二天三天四天等等，直到工作日结束。
      while (true) {
        HolidayCalendar compareHolidayCalendar = null;
        for (HolidayCalendar holidayCalendar : holidayCalendarList) {
          if (holidayCalendar.getStartTime() <= secondDay && holidayCalendar.getEndTime() >= secondDay && holidayCalendar.getHolidayTypeEnum().getCode() == 1) {
            // 对类型是假期的，并且在假期中的日期进行天数累加。
            dataDays++;
            // 此对象用于退出while循环是使用
            compareHolidayCalendar = holidayCalendar;
            // 重新计算下一天的时间
            secondDay = secondDay + 1000 * 60 * 60 * 24;
            break;
          }
        }
        if (compareHolidayCalendar == null) {
          // 遇到工作日时，退出while循环。
          dataDays++;
          break;
        }
      }
    }
    //获取短期天数
    String sysParameter = super.getSysParameter("FILE_FORECAST_DAYS", "3");
    int dqSysParameter = Integer.parseInt(sysParameter);
    if (dataDays < dqSysParameter) {
      dataDays = dqSysParameter;
    }
    return dataDays;
  }

  /**
   * 获取计划数据天数
   *
   * @return 查询数据的天数
   */
  public int calculateJHDataDays(Long todayTime) {
   /* long todayTime = DateUtil.beginOfDay(new Date()).getTime();*/
    // 获取日历中所有记录
    List<HolidayCalendar> holidayCalendarList = holidayCalendarRepository.findAll();
    // 今日是否生成文件，true为生成。
    boolean todayGenerateFile = true;
    // 今日是否特殊工作日，false为不是特殊日。
    boolean todaySpecialWorkDay = false;
    // 下一天是否特殊工作日，false为不是特殊日。
    boolean nextSpecialWorkDay = false;
    // 计算下一天的日期
    Long secondDay = DateUtil.offset(DateUtil.beginOfDay(new Date(todayTime)), DateField.DAY_OF_MONTH, 1).getTime();
    // 循环获取条件变量
    for (HolidayCalendar holidayCalendar : holidayCalendarList) {
      Long startTime = holidayCalendar.getStartTime();
      Long endTime = holidayCalendar.getEndTime();
      // 获取日历中的数据类型 1假期 2特殊工作日【只可以是礼拜六和礼拜日】
      int type = holidayCalendar.getHolidayTypeEnum().getCode();
      if (startTime <= todayTime && endTime >= todayTime) {
        if (type == 1) {
          // 今日是假期，将变量设置成false，代表不生成文件。
          todayGenerateFile = false;
        } else {
          // 今日是特殊日。
          todaySpecialWorkDay = true;
        }
      }
      if (startTime <= secondDay && endTime >= secondDay && type == 2) {
        // 第二天是特殊工作日
        nextSpecialWorkDay = true;
      }
    }
    Calendar cal = Calendar.getInstance();
    cal.setTimeInMillis(todayTime);
    if ((cal.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY || cal.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) && todaySpecialWorkDay == false) {
      todayGenerateFile = false;
    }

    // 定义数据天数变量
    int dataDays = 0;
    if (todayGenerateFile || todaySpecialWorkDay) {
      // 生成文件
      if (!nextSpecialWorkDay) {
        // 遍历第二天三天四天等等，直到工作日结束。
        while (true) {
          HolidayCalendar compareHolidayCalendar = null;
          for (HolidayCalendar holidayCalendar : holidayCalendarList) {
            if (holidayCalendar.getStartTime() <= secondDay && holidayCalendar.getEndTime() >= secondDay) {
              // 获取下一天的日历对象
              compareHolidayCalendar = holidayCalendar;
              break;
            }
          }
          if (compareHolidayCalendar != null) {
            if (compareHolidayCalendar.getHolidayTypeEnum().getCode() == 2) {
              // 特殊工作日
              dataDays++;
              break;
            }
          } else {
            // 如果第二天是周六或者周日
            cal.setTimeInMillis(secondDay);
            if (cal.get(Calendar.DAY_OF_WEEK) != Calendar.SATURDAY && cal.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY) {
              dataDays++;
              break;
            }
          }
          // 假期
          dataDays++;
          secondDay = secondDay + 1000 * 60 * 60 * 24;
        }
      }
      // 第二天是特殊工作日，生成第二天的数据,天数从参数获取
      String sysParameter = getSysParameter("FILE_POWERPLAN_DAYS", "1");
      int jhSysParameter = Integer.parseInt(sysParameter);
      if (dataDays < jhSysParameter) {
        dataDays = jhSysParameter;
      }
    }
    return dataDays;
  }

  /**
   * 计算文件理论上报数
   * @param moment 时间间隔
   * @return
   */
  public BigDecimal calculateFileNum(BigDecimal moment,Long startDate,Long endDate) {
    BigDecimal startTime = new BigDecimal(startDate);
    BigDecimal endTime = new BigDecimal(endDate);
    BigDecimal radioBig = new BigDecimal("1");
    return endTime.subtract(startTime).divideToIntegralValue(moment).add(radioBig);
  }





}
