package cn.cecep.talroad.service.strategy.support.sn;

import cn.cecep.talroad.common.core.utils.DateUtils;
import cn.cecep.talroad.common.core.utils.StringUtils;
import cn.cecep.talroad.common.redis.service.RedisService;
import cn.cecep.talroad.domain.AmProblemFi;
import cn.cecep.talroad.domain.BasThresholdConfig;
import cn.cecep.talroad.domain.MonDevSnReal;
import cn.cecep.talroad.domain.STaskYieldTrend;
import cn.cecep.talroad.enums.ProblemTypeEnum;
import cn.cecep.talroad.enums.ThresHoldConfigEnum;
import cn.cecep.talroad.enums.TimeTypeEnum;
import cn.cecep.talroad.enums.YieldTypeEnum;
import cn.cecep.talroad.service.IBasDeviceParamService;
import cn.cecep.talroad.service.IBasThresholdConfigService;
import cn.cecep.talroad.service.IMonDevSnRealService;
import cn.cecep.talroad.service.strategy.YieldStrategyAdapter;
import cn.cecep.talroad.vo.BasDeviceParamVo;
import cn.cecep.talroad.vo.FactoryDeviceVo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @Auther: cjh
 * @Date: 2023-04-03 11:57
 * @Class: SnSnyCountAdapter
 * @Deseription:
 * @Version V1.0
 */
@Slf4j
@Service("SNSNYSNY")
public class SnSnyCountAdapter extends YieldStrategyAdapter {

    @Autowired
    private IMonDevSnRealService monDevSnRealService;
    @Autowired
    private IBasDeviceParamService basDeviceParamService;
    @Autowired
    private IBasThresholdConfigService basThresholdConfigService;
    @Autowired
    private RedisService redisService;

    /**
     * 查询设备监测数据
     *
     * @param vo
     * @param tempDate
     * @param flag     是否处理历史数据  true 是 false 否
     */
    @Override
    protected void doQueryYieldData(FactoryDeviceVo vo, Date tempDate, boolean flag) {

        Date lastHour = DateUtils.addHours(tempDate, -1);
        Date lastHourBegin = DateUtils.parseFormatDate("yyyy-MM-dd HH:00:00", lastHour);
        Date lastHourEnd = DateUtils.parseFormatDate("yyyy-MM-dd HH:59:59", lastHour);
        QueryWrapper<MonDevSnReal> snQueryWrapper = new QueryWrapper<>();
        snQueryWrapper.eq("factory_id", vo.getFactoryId());
        snQueryWrapper.eq("device_id", vo.getDeviceId());
        snQueryWrapper.eq("time_type", TimeTypeEnum.REAL.getCode());
        snQueryWrapper.between("data_time", lastHourBegin, lastHourEnd);
        snQueryWrapper.orderByAsc("data_time");
        List<MonDevSnReal> realList = monDevSnRealService.list(snQueryWrapper);
        if (realList != null && realList.size() > 0) {
            BasThresholdConfig config = basThresholdConfigService.selectByParamCode(ThresHoldConfigEnum.SN01.getCode());
            //计算1小时内生产端加料量平均值
            Double avgHour = realList.stream().filter(r->StringUtils.isNotEmpty(r.getSs100())).mapToDouble(m -> Double.valueOf(m.getSs100()))
                .average().getAsDouble();
            BigDecimal hourCl = new BigDecimal(avgHour);
            // 水泥行业产量计算阈值，1.56为经验值
            BigDecimal jyz = config.getThresholdValue().setScale(2, RoundingMode.HALF_UP);
            //计算水泥熟料产量小时值
            BigDecimal slHour = hourCl.divide(jyz, 4, RoundingMode.HALF_UP);

            Date analyseTime = DateUtils.parseFormatDate("yyyy-MM-dd HH", lastHour);
            //保存水泥产量
            super.doSaveAnalysisCementData(vo, avgHour, slHour, analyseTime);
            //保存产量趋势
            STaskYieldTrend trend = super.doBuildTrenData(vo.getFactoryId(), vo.getDeviceId(), YieldTypeEnum.SN_001, lastHourBegin, slHour, flag);
            // 保存产量
            super.doSaveYieldTrendData(trend);
            try {
                // 布袋除尘器，压差是否频繁超高分析
                this.doProcessPressData(vo, realList, lastHourBegin);
            } catch (Exception e) {
                log.error("水泥布袋除尘器，压差是否频繁超高分析异常"+e.getMessage());
            }
        }
    }


    /**
     * 布袋除尘器，压差是否频繁超高分析
     *
     * @param vo
     * @param currList
     * @param lastHour
     */
    private void doProcessPressData(FactoryDeviceVo vo, List<MonDevSnReal> currList, Date lastHour) {
        List<AmProblemFi> alarmList = new ArrayList<>();
        Date curr = new Date();

        if (currList != null && currList.size() > 0) {
            List<BasDeviceParamVo> paramsList = basDeviceParamService.selectBasDeviceParamByCondition(vo.getDeviceId(), Arrays.asList("sz101","sz103","sz105"));
            //布袋除尘器，压差是否频繁超高
            processBdData(vo, currList, lastHour, alarmList, curr, paramsList);
            //静态除尘器
            processJdData(vo, currList, lastHour, alarmList, curr, paramsList);
            // 批量保存报警数据
            super.doSaveBatchAlarm(alarmList);
        }
    }

    /**
     * 布袋除尘器，压差是否频繁超高
     * @param vo
     * @param currList
     * @param lastHour
     * @param alarmList
     * @param curr
     * @param paramsList
     */
    private void processBdData(FactoryDeviceVo vo, List<MonDevSnReal> currList, Date lastHour, List<AmProblemFi> alarmList, Date curr, List<BasDeviceParamVo> paramsList) {
        String overFlag = redisService.getCacheObject(ProblemTypeEnum.PRESS_OVER_STAND.getCode() + vo.getDeviceId());
        String lowFlag = redisService.getCacheObject(ProblemTypeEnum.PRESS_LOW_STAND.getCode() + vo.getDeviceId());
        if(StringUtils.isEmpty(overFlag)||StringUtils.isEmpty(lowFlag)) {

            //超高次数
            BasThresholdConfig hCount = basThresholdConfigService.selectByParamCode(ThresHoldConfigEnum.H01.getCode());
            //连续累计超高时间
            BasThresholdConfig hTime = basThresholdConfigService.selectByParamCode(ThresHoldConfigEnum.H02.getCode());
            //报警间隔时长
            BasThresholdConfig hAlarm = basThresholdConfigService.selectByParamCode(ThresHoldConfigEnum.H03.getCode());
            // 过低次数
            BasThresholdConfig lCount = basThresholdConfigService.selectByParamCode(ThresHoldConfigEnum.L01.getCode());
            //连续累计过低时间
            BasThresholdConfig lTime = basThresholdConfigService.selectByParamCode(ThresHoldConfigEnum.L02.getCode());
            //报警间隔时长
            BasThresholdConfig lAlarm = basThresholdConfigService.selectByParamCode(ThresHoldConfigEnum.L03.getCode());

            AtomicInteger m = new AtomicInteger();//超高次数
            AtomicInteger h = new AtomicInteger();//连续累计超高时间
            AtomicInteger n = new AtomicInteger();//超低次数
            AtomicInteger l = new AtomicInteger();//连续累计超高时间

            AtomicReference<BigDecimal> maxStandValue = new AtomicReference<>(new BigDecimal(0));
            AtomicReference<BigDecimal> minStandValue = new AtomicReference<>(new BigDecimal(0));
            if (paramsList != null && paramsList.size() > 0) {
                paramsList.forEach(p -> {
                    if ("sz101".equals(p.getParamCodeSvc())) {
                        maxStandValue.set(p.getMaxValue());
                        minStandValue.set(p.getMinValue());
                    }
                });
            }
            if (maxStandValue.get().doubleValue() > 0 || minStandValue.get().doubleValue() > 0) {
                currList.forEach(real -> {
                    if (StringUtils.isEmpty(overFlag)&&maxStandValue.get().doubleValue() > 0) {
                        if (StringUtils.isNotEmpty(real.getSz101()) && Double.valueOf(real.getSz101()) > maxStandValue.get().doubleValue()) {
                            m.getAndIncrement();
                            h.getAndIncrement();
                        } else {
                            // 压差未超高，则重新计数
                            if (h.get() < hTime.getThresholdValue().intValue()) {
                                h.set(0);
                            }
                        }
                    }
                    if (StringUtils.isEmpty(lowFlag)&&minStandValue.get().doubleValue() > 0) {
                        if (StringUtils.isNotEmpty(real.getSz101()) && Double.valueOf(real.getSz101()) < minStandValue.get().doubleValue()) {
                            n.getAndIncrement();
                            l.getAndIncrement();
                        } else {
                            // 压差未超低，则重新计数
                            if (l.get() < lTime.getThresholdValue().intValue()) {
                                l.set(0);
                            }
                        }
                    }
                });
            }
            if (StringUtils.isEmpty(overFlag)) {
                // 满足条件其中一个条件，即超高报警
                if (m.get() >= hCount.getThresholdValue().intValue() || h.get() >= hTime.getThresholdValue().intValue()) {
                    StringBuffer sb = new StringBuffer();
                    sb.append(vo.getDeviceName()).append("窑尾布袋除尘器于").append(DateUtils.parseDateToStr("yyyy-MM-dd HH:00:00", lastHour)).append("开始，进出口压差频繁超过设计标准值高限，可能导致除尘效率变低，存在颗粒物浓度超标风险，请及时排查。");
                    AmProblemFi problemFi = getAmProblemFi(vo, curr, sb, ProblemTypeEnum.PRESS_OVER_STAND,"压差,二次电流,二次电压","sz101,sz103,sz105");
                    alarmList.add(problemFi);
                    redisService.setCacheObject(ProblemTypeEnum.PRESS_OVER_STAND.getCode() + vo.getDeviceId(), vo.getDeviceId(), hAlarm.getThresholdValue().longValue(), TimeUnit.HOURS);
                }
            }
            if(StringUtils.isEmpty(lowFlag)){
                if (n.get() >= lCount.getThresholdValue().intValue() || l.get() >= lTime.getThresholdValue().intValue()) {
                    StringBuffer sb = new StringBuffer();
                    sb.append(vo.getDeviceName()).append("窑尾布袋除尘器于").append(DateUtils.parseDateToStr("yyyy-MM-dd HH:00:00", lastHour)).append("开始，进出口压差频繁低于设计标准值下限，可能导致除尘效率变低，请及时排查。");
                    AmProblemFi problemFi = getAmProblemFi(vo, curr, sb, ProblemTypeEnum.PRESS_LOW_STAND,"压差,二次电流,二次电压","sz101,sz103,sz105");
                    alarmList.add(problemFi);
                    redisService.setCacheObject(ProblemTypeEnum.PRESS_LOW_STAND.getCode() + vo.getDeviceId(), vo.getDeviceId(), lAlarm.getThresholdValue().longValue(), TimeUnit.HOURS);
                }
            }
        }
    }

    /**
     * 静电除尘
     *
     * @param vo
     * @param currList
     * @param lastHour
     * @param alarmList
     * @param curr
     * @param paramsList
     */
    private void processJdData(FactoryDeviceVo vo, List<MonDevSnReal> currList, Date lastHour, List<AmProblemFi> alarmList, Date curr, List<BasDeviceParamVo> paramsList) {

        String highFlag = redisService.getCacheObject(ProblemTypeEnum.JDCC_GG.getCode() + vo.getDeviceId());
        String ycFlag = redisService.getCacheObject(ProblemTypeEnum.JDCC_YC.getCode() + vo.getDeviceId());
        String lowFlag = redisService.getCacheObject(ProblemTypeEnum.JDCC_GD.getCode() + vo.getDeviceId());
        if(StringUtils.isEmpty(highFlag)||StringUtils.isEmpty(ycFlag)||StringUtils.isEmpty(lowFlag)) {

            //报警间隔时长
            BasThresholdConfig alarm = basThresholdConfigService.selectByParamCode(ThresHoldConfigEnum.J01.getCode());
            //超标次数
            BasThresholdConfig hCount = basThresholdConfigService.selectByParamCode(ThresHoldConfigEnum.J02.getCode());
            //连续累计超高时间
            BasThresholdConfig hTime = basThresholdConfigService.selectByParamCode(ThresHoldConfigEnum.J03.getCode());
            // 连续累计过低时间
            BasThresholdConfig lTime = basThresholdConfigService.selectByParamCode(ThresHoldConfigEnum.J04.getCode());

            AtomicInteger m = new AtomicInteger();//超标次数
            AtomicInteger h = new AtomicInteger();//连续累计超高时间
            AtomicInteger l = new AtomicInteger();//连续累计过低时间

            AtomicReference<BigDecimal> pressStandValue = new AtomicReference<>(new BigDecimal(0));
            AtomicReference<BigDecimal> elecStandValue = new AtomicReference<>(new BigDecimal(0));
            if (paramsList != null && paramsList.size() > 0) {
                paramsList.forEach(p -> {
                    //二次电压
                    if ("sz105".equals(p.getParamCodeSvc())) {
                        pressStandValue.set(p.getNormalValue());
                    }
                    //二次电流
                    if ("sz103".equals(p.getParamCodeSvc())) {
                        elecStandValue.set(p.getNormalValue());
                    }
                });
            }

            if (pressStandValue.get().doubleValue() > 0 || elecStandValue.get().doubleValue() > 0) {
                Set<String> set = new HashSet<>();
                Set<String> bset = new HashSet<>();
                Double maxElec = elecStandValue.get().multiply(new BigDecimal(1.2)).setScale(2, RoundingMode.HALF_UP).doubleValue();
                Double minElec = elecStandValue.get().multiply(new BigDecimal(0.5)).setScale(2, RoundingMode.HALF_UP).doubleValue();
                Double minPress = pressStandValue.get().multiply(new BigDecimal(0.5)).setScale(2, RoundingMode.HALF_UP).doubleValue();
                Double mElec = elecStandValue.get().multiply(new BigDecimal(1.1)).setScale(2, RoundingMode.HALF_UP).doubleValue();
                Double nElec = elecStandValue.get().multiply(new BigDecimal(0.9)).setScale(2, RoundingMode.HALF_UP).doubleValue();
                Double mPress = pressStandValue.get().multiply(new BigDecimal(1.1)).setScale(2, RoundingMode.HALF_UP).doubleValue();
                Double nPress = pressStandValue.get().multiply(new BigDecimal(0.9)).setScale(2, RoundingMode.HALF_UP).doubleValue();

                currList.forEach(real -> {
                    if (StringUtils.isNotEmpty(real.getSz103())) {
                        // 二次电流大于额定电流1.2倍，二次电压小于额定电压0.5
                        if (StringUtils.isNotEmpty(real.getSz105()) && Double.valueOf(real.getSz103()) > maxElec && Double.valueOf(real.getSz105()) < minPress) {
                            h.getAndIncrement();
                        }
                        // 二次电流小于额定电流0.9 或二次电流大于额定电流1.1
                        if (Double.valueOf(real.getSz103()) < nElec || Double.valueOf(real.getSz103()) > mElec) {
                            m.getAndIncrement();
                            set.add("二次电流");
                            bset.add("二次电流额定电流");
                        }
                        //二次电压正常,二次电流小于额定电流0.5
                        if (StringUtils.isNotEmpty(real.getSz105()) && (Double.valueOf(real.getSz105()) > nPress && Double.valueOf(real.getSz105()) < mPress)
                            && Double.valueOf(real.getSz103()) < minElec) {
                            l.getAndIncrement();
                        } else {
                            if (l.get() < lTime.getThresholdValue().intValue()) {
                                l.set(0);
                            }
                        }
                    }
                    // 二次电压小于额定电压0.9 或二次电压大于额定电压1.1
                    if (StringUtils.isNotEmpty(real.getSz105()) && (Double.valueOf(real.getSz105()) < nPress || Double.valueOf(real.getSz105()) > mPress)) {
                        m.getAndIncrement();
                        set.add("二次电压");
                        bset.add("二次电压额定电压");
                    }

                });
                if (StringUtils.isEmpty(highFlag)) {
                    //二次电流超过额定电流1.2倍且二次电压小于额定电压0.5倍的时间持续10分钟以上,生成报警记录
                    if (h.get() > hTime.getThresholdValue().intValue()) {
                        StringBuffer sb = new StringBuffer();
                        sb.append(vo.getDeviceName()).append("静电除尘器于").append(DateUtils.parseDateToStr("yyyy-MM-dd HH:00:00", lastHour))
                            .append("开始，二次电流超过额定电流1.2倍且二次电压小于额定电压0.5倍的时间持续10分钟以上，可能导致除尘效率变低，存在颗粒物浓度超标风险，请及时排查。");
                        AmProblemFi problemFi = getAmProblemFi(vo, curr, sb, ProblemTypeEnum.JDCC_GG,"压差,二次电流,二次电压","sz101,sz103,sz105");
                        alarmList.add(problemFi);
                        redisService.setCacheObject(ProblemTypeEnum.JDCC_GG.getCode() + vo.getDeviceId(), vo.getDeviceId(), alarm.getThresholdValue().longValue(), TimeUnit.HOURS);
                    }
                }
                if (StringUtils.isEmpty(ycFlag)) {
                    //二次电压小于额定电压0.9 或二次电压大于额定电压1.1
                    if (m.get() >= hCount.getThresholdValue().intValue()) {
                        StringBuffer sb = new StringBuffer();
                        String paramStr = StringUtils.join(",", set);
                        String standStr = StringUtils.join(",", bset);
                        sb.append(vo.getDeviceName()).append("静电除尘器于").append(DateUtils.parseDateToStr("yyyy-MM-dd HH:00:00", lastHour))
                            .append("开始，报警指标" + paramStr + "超过对比标准" + standStr + "10%以上次数超过" + hCount.getThresholdValue().intValue() + "次，可能导致除尘效率变低，存在颗粒物浓度超标风险，请及时排查。");
                        AmProblemFi problemFi = getAmProblemFi(vo, curr, sb, ProblemTypeEnum.JDCC_YC,"压差,二次电流,二次电压","sz101,sz103,sz105");
                        alarmList.add(problemFi);
                        redisService.setCacheObject(ProblemTypeEnum.JDCC_YC.getCode() + vo.getDeviceId(), vo.getDeviceId(), alarm.getThresholdValue().longValue(), TimeUnit.HOURS);
                    }
                }

                if (StringUtils.isEmpty(lowFlag)) {
                    //二次电压正常 且二次电流小于额定电流0.5
                    if (l.get() >= lTime.getThresholdValue().intValue()) {
                        StringBuffer sb = new StringBuffer();
                        sb.append(vo.getDeviceName()).append("静电除尘器于").append(DateUtils.parseDateToStr("yyyy-MM-dd HH:00:00", lastHour))
                            .append("开始，二次电压正常，但二次电流过低，时长超过" + lTime.getThresholdValue().intValue() + "分钟,可能导致除尘效率变低，存在颗粒物浓度超标风险，请及时排查。");
                        AmProblemFi problemFi = getAmProblemFi(vo, curr, sb, ProblemTypeEnum.JDCC_GD,"压差,二次电流,二次电压","sz101,sz103,sz105");
                        alarmList.add(problemFi);
                        redisService.setCacheObject(ProblemTypeEnum.JDCC_GD.getCode() + vo.getDeviceId(), vo.getDeviceId(), alarm.getThresholdValue().longValue(), TimeUnit.HOURS);
                    }
                }
            }
        }
    }

    /**
     * 获取报警数据
     *
     * @param vo
     * @param curr
     * @param sb
     * @param pressOverStand
     * @return
     */
    private AmProblemFi getAmProblemFi(FactoryDeviceVo vo, Date curr, StringBuffer sb, ProblemTypeEnum pressOverStand,String param,String paramProperty) {
        AmProblemFi problemFi = super.doProcessAlaram(vo.getFactoryId(), vo.getDeviceId(), vo.getDeviceName(), curr, new Date(), pressOverStand, sb.toString(), param, paramProperty);
        return problemFi;
    }
}
