package com.jiayue.ipfcst.dataexchange.dataprocesser;

import com.jiayue.ipfcst.common.core.util.SpringContextHolder;
import com.jiayue.ipfcst.common.data.entity.FakeDataRecord;
import com.jiayue.ipfcst.common.data.entity.ProtocolGatherDataPoint;
import com.jiayue.ipfcst.common.data.service.FakeDataRecordService;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.joda.time.Minutes;
import java.math.BigDecimal;
import java.util.Random;

/**
 * 数据审查类
 *
 * @author: xiuwei
 */
@Slf4j
public class DataAuditor {


  /**
   * 常量  随机数
   */
  private final static Random random = new Random();


  /**
   * 审计数据
   * 对数据的空死兀越进行判断
   *
   * @param protocolGatherDataPoint 点位信息，checkDataRecode 数据记录，value 数据值
   * @param checkDataRecode         检查数据对象
   * @param value                   值
   * @return BigDecimal  审核之后返回的合理数值
   */
  public static BigDecimal auditData(ProtocolGatherDataPoint protocolGatherDataPoint, CheckDataRecode checkDataRecode, @NonNull BigDecimal value) {
    try {
      checkDataRecode.initValue(value,protocolGatherDataPoint.getMaxValue(),protocolGatherDataPoint.getMinValue());
      FakeDataRecord.IrrationalDataType audit = isRational(protocolGatherDataPoint, checkDataRecode, value);
      //当数据合理时
      if (audit== FakeDataRecord.IrrationalDataType.Rational) {
        checkDataRecode.setTime(DateTime.now());
        checkDataRecode.setOriginalVal(value);
        checkDataRecode.setUsedVal(value);
      } else {
        //当数据不合理时
        FakeDataRecord fakeDataRecord=new FakeDataRecord();
        fakeDataRecord.setEquipmentAttribute(protocolGatherDataPoint.getEquipmentAttribute());
        fakeDataRecord.setEquipmentNo(protocolGatherDataPoint.getEquipmentId());
        fakeDataRecord.setIrrationalDataType(audit);
        fakeDataRecord.setTime(DateTime.now().withSecondOfMinute(0).toDate());
        fakeDataRecord.setOriginalValue(value.setScale(2,BigDecimal.ROUND_HALF_UP));
        value = generateData(protocolGatherDataPoint, checkDataRecode, value);
        fakeDataRecord.setCorrectionValue(value.setScale(2,BigDecimal.ROUND_HALF_UP));
        SpringContextHolder.getBean(FakeDataRecordService.class).save(fakeDataRecord);
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    return value.setScale(2,BigDecimal.ROUND_HALF_UP);
  }


  /**
   * 单个或几个数据错误生成数据，但是当数据长时间采集不到或者数据长时间异常 那么生成逻辑将极为复杂
   * 暂时还没想到好的解决方法。
   *
   * @param protocolGatherDataPoint 点位信息，checkDataRecode 数据记录，value 数据值
   * @return BigDecimal  根据数值的限制生成一个假数据
   **/
  private static BigDecimal generateData(ProtocolGatherDataPoint protocolGatherDataPoint, CheckDataRecode checkDataRecode, BigDecimal value) {
    BigDecimal fluctuate;
    BigDecimal changedVal;
    if ((value == null && random.nextDouble() > 0.5) || value.compareTo(checkDataRecode.getUsedVal()) > 0) {
      //向上浮动 正值
      fluctuate = BigDecimal.valueOf(protocolGatherDataPoint.getMaxValue()).subtract(checkDataRecode.getUsedVal()).multiply(BigDecimal.valueOf(random.nextDouble()).multiply(BigDecimal.valueOf(0.1)));
      if(fluctuate.compareTo(BigDecimal.ZERO)==-1){
        fluctuate = BigDecimal.valueOf(protocolGatherDataPoint.getMaxValue()).subtract(checkDataRecode.getUsedVal()).multiply(BigDecimal.valueOf(random.nextDouble()).multiply(BigDecimal.valueOf(0.1)).add(BigDecimal.ONE));
      }
    } else {
      //向下浮动  负值
      fluctuate = BigDecimal.valueOf(protocolGatherDataPoint.getMinValue()).subtract(checkDataRecode.getUsedVal()).multiply(BigDecimal.valueOf(random.nextDouble()).multiply(BigDecimal.valueOf(0.1)));
      if(fluctuate.compareTo(BigDecimal.ZERO)==1){
        fluctuate = BigDecimal.valueOf(protocolGatherDataPoint.getMaxValue()).subtract(checkDataRecode.getUsedVal()).multiply(BigDecimal.valueOf(random.nextDouble()).multiply(BigDecimal.valueOf(0.1)).add(BigDecimal.ONE));
      }
    }
    if (fluctuate.abs().compareTo(BigDecimal.valueOf(protocolGatherDataPoint.getMaxChangeValue())) == 1) {
      fluctuate = fluctuate.divideAndRemainder(BigDecimal.valueOf(protocolGatherDataPoint.getMaxChangeValue()))[1];
    }
    changedVal = checkDataRecode.getUsedVal().add(fluctuate).setScale(2, BigDecimal.ROUND_HALF_UP);
    log.warn("生成的假数据为:{}", changedVal);
    checkDataRecode.setUsedVal(changedVal);
    checkDataRecode.setTime(DateTime.now());
    checkDataRecode.setOriginalVal(value);
    return checkDataRecode.getUsedVal();

  }


  /**
   * 数据是否超上限
   *
   * @param protocolGatherDataPoint 点位
   * @param checkDataRecode         历史记录
   * @param value                   传入值
   * @return 是否超上限
   */
  private static boolean isOverMax(ProtocolGatherDataPoint protocolGatherDataPoint, CheckDataRecode checkDataRecode, BigDecimal value) {
    if (protocolGatherDataPoint.getMaxValue() != null && BigDecimal.valueOf(protocolGatherDataPoint.getMaxValue()).compareTo(value) == -1) {
      log.warn("{}-{}-{}点位数据超过上限，数值为{},上限为{}", protocolGatherDataPoint.getEquipmentAttribute().getEquipmentType(),
        protocolGatherDataPoint.getEquipmentId(), protocolGatherDataPoint.getEquipmentAttribute().getFieldName(), value, protocolGatherDataPoint.getMaxValue());
      return true;
    } else {
      return false;
    }
  }

  /**
   * 数据是否越下限
   *
   * @param protocolGatherDataPoint 点位
   * @param checkDataRecode         历史记录
   * @param value                   传入值
   * @return 是否越下限
   */
  private static boolean isLowerMin(ProtocolGatherDataPoint protocolGatherDataPoint, CheckDataRecode checkDataRecode, BigDecimal value) {
    if (protocolGatherDataPoint.getMinValue() != null && BigDecimal.valueOf(protocolGatherDataPoint.getMinValue()).compareTo(value) == 1) {
      log.warn("{}-{}-{}点位数据超过下限，数值为{},下限为{}", protocolGatherDataPoint.getEquipmentAttribute().getEquipmentType(),
        protocolGatherDataPoint.getEquipmentId(), protocolGatherDataPoint.getEquipmentAttribute().getFieldName(), value, protocolGatherDataPoint.getMinValue());
      return true;
    } else {
      return false;
    }
  }

  /**
   * 数据是否是死数据
   *
   * @param protocolGatherDataPoint 点位
   * @param checkDataRecode         历史记录
   * @param value                   传入值
   * @return 是否是死值
   */
  private static boolean isDeadVal(ProtocolGatherDataPoint protocolGatherDataPoint, CheckDataRecode checkDataRecode, BigDecimal value) {
    DateTime dateTime = DateTime.now();
    boolean flag = false;
    if (protocolGatherDataPoint.getMaxDuration() != null) {
      if (checkDataRecode.getOriginalVal() != null && checkDataRecode.getOriginalVal().equals(value)) {
        if (Minutes.minutesBetween(checkDataRecode.getDeadDataStartTime(), dateTime).getMinutes() > protocolGatherDataPoint.getMaxDuration()) {
          log.warn("{}-{}-{}点位数据死值，数值为{},开始时间为{}", protocolGatherDataPoint.getEquipmentAttribute().getEquipmentType(),
            protocolGatherDataPoint.getEquipmentId(), protocolGatherDataPoint.getEquipmentAttribute().getFieldName(), value,
            checkDataRecode.getDeadDataStartTime().toString("HH:mm:ss:sss"));
          flag = true;
        }
      } else {
        checkDataRecode.setDeadDataStartTime(dateTime);
      }
    }
    return flag;
  }

  /**
   * 数据是否为突变数据
   *
   * @param protocolGatherDataPoint 点位
   * @param checkDataRecode         历史记录
   * @param value                   传入值
   * @return boolean 是否突变
   */
  private static boolean isSuddenChange(ProtocolGatherDataPoint protocolGatherDataPoint, CheckDataRecode checkDataRecode, BigDecimal value) {
    if (protocolGatherDataPoint.getMaxChangeValue() != null && value.subtract(checkDataRecode.getUsedVal()).abs().compareTo(BigDecimal.valueOf(protocolGatherDataPoint.getMaxChangeValue())) == 1) {
      log.warn("{}-{}-{}点位数据超过突变极限，数值为{},上一个值为{},突变极限为{}", protocolGatherDataPoint.getEquipmentAttribute().getEquipmentType(),
        protocolGatherDataPoint.getEquipmentId(), protocolGatherDataPoint.getEquipmentAttribute().getFieldName(), value, checkDataRecode.getUsedVal()
        , protocolGatherDataPoint.getMaxChangeValue());
      return true;
    } else {
      return false;
    }
  }

  /**
   * 数据是否合理
   *
   * @param protocolGatherDataPoint 点位
   * @param checkDataRecode         历史记录
   * @param value                   传入值
   * @return boolean  判断数据是否合理 总结了空 死 越 兀
   */
  private static FakeDataRecord.IrrationalDataType isRational(ProtocolGatherDataPoint protocolGatherDataPoint, CheckDataRecode checkDataRecode, BigDecimal value) {
    if( isOverMax(protocolGatherDataPoint, checkDataRecode, value)){
      return FakeDataRecord.IrrationalDataType.OverMax;
    }
    if(isLowerMin(protocolGatherDataPoint, checkDataRecode, value)){
      return FakeDataRecord.IrrationalDataType.LowerMin;
    }
    if(isDeadVal(protocolGatherDataPoint, checkDataRecode, value)){
      return FakeDataRecord.IrrationalDataType.DeadVal;
    }
    if(isSuddenChange(protocolGatherDataPoint, checkDataRecode, value)){
      return FakeDataRecord.IrrationalDataType.SuddenChange;
    }
    return FakeDataRecord.IrrationalDataType.Rational;
  }

}
