package com.mvc.service.alarm;

import com.mvc.constant.GasAlarmType;
import com.mvc.constant.GasMeterSpec;
import com.mvc.entity.TAiread;
import com.mvc.model.*;
import com.mvc.model.alarm.TAlarmNoticeDetailModel;
import com.mvc.model.alarm.TAlarmNoticeModel;
import com.mvc.model.alarm.TAlarmRecordModel;
import com.mvc.othermodel.QuantileModel;
import com.mvc.service.copy.TAireadService;
import com.mvc.service.taizhang.StationAccService;
import com.mvc.service.taizhang.TAicbqCfgService;
import com.mvc.utils.DateUtil;
import com.mvc.utils.ImageUtil;
import com.mvc.utils.NotifyUtil;
import com.mvc.utils.NumberUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.time.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 邮件通知管理(针对太原燃气)
 */
@Component
public class AlarmNoticeMana {
    private static Logger log = LoggerFactory.getLogger(AlarmNoticeMana.class);

    @Resource
    private TAlarmNoticeService alarmNoticeService;

    @Resource
    private TAireadService aireadService;

    @Resource
    private TAlarmRecordService alarmRecordService;

    @Resource
    private StationAccService stationAccService;

    @Resource
    private NotifyUtil notifyUtil;

    @Resource
    private TAicbqCfgService tAicbqCfgService;

    public static List<TAlarmNoticeModel> mAlarmNoticeModels;

    private static String TYMQ_UNIT_CODE = "39ec081ceb9740afad8fe14c4ea1349f";//太原天然气单位代码

    /**
     * 分拣报警数据
     * 根据口径规格计算
     *
     * @param serverReadModel
     */
    public void sortingAlarmData(TServerReadModel serverReadModel) {
        if (serverReadModel == null || StringUtils.isEmpty(serverReadModel.getStationCode())) {
            writeLog(serverReadModel.getStationCode(), "serverReadModel 或 = null");
            return;
        }

        //本次服务侧读数对应的TAireadModel
        TAireadModel aireadModel = aireadService.getTaireadByAireadId(serverReadModel.getAireadId());

        //处理订阅报警数据
        handleSubscribeAlarm(aireadModel);

        StationAccModel accModel = stationAccService.findStationAccByStationCode(serverReadModel.getStationCode());
        if (accModel == null) {
            writeLog(serverReadModel.getStationCode(), "没有站点信息");
            return;
        }

        //非太原煤气的表,不处理
        if (!accModel.getUnitCode().equals(TYMQ_UNIT_CODE) && !accModel.getVisUnitCodes().contains(TYMQ_UNIT_CODE)) {
            writeLog(serverReadModel.getStationCode(), "非太原燃气设备");
            return;
        }

        //根据四分位处理动态报警 2024-04-03 guozhao
        handleDynamicRange(aireadModel);

        //没有有效配置
        TAicbqCfgModel cfgModel = tAicbqCfgService.findTAicbqCfgModel("", serverReadModel.getStationCode(), "1");
        if (cfgModel == null || cfgModel.getDnType() == null) {
            writeLog(serverReadModel.getStationCode(), "没有有效配置信息");
            return;
        }

        GasMeterSpec meterSpec = GasMeterSpec.findGasMeterSpecByQMax(cfgModel.getDnType());
        //根据配置未匹配到规格
        if (meterSpec == null) {
            writeLog(serverReadModel.getStationCode(), "未匹配到规格(口径)");
            return;
        }

        Date lstTime = DateUtil.getOtherDateByHour(aireadModel.getSampleTime(), -1);
        //至少一小时前最新读数
        TAiread lstReadModel = aireadService.findlastTAireadBydatatime(aireadModel.getStationCode(), lstTime);

        if (aireadModel.getResultValue() == null || lstReadModel == null || lstReadModel.getResultValue() == null) {//读数有问题
            writeLog(serverReadModel.getStationCode(), "当前读数或上次读数无效");
            return;
        }

        //新来的数据,未识别或上条数未识别且未审核的情况下,计算会有问题 2024-02-23 guozhao
        if (aireadModel.getResultValue().compareTo(BigDecimal.ZERO) == 0 || lstReadModel.getResultValue().compareTo(BigDecimal.ZERO) == 0) {
            writeLog(serverReadModel.getStationCode(), "当前读数或上次 = 0");
            return;
        }

        //用量差值
        BigDecimal dValue = aireadModel.getResultValue().subtract(lstReadModel.getResultValue());
        //时间差值
        Double dHous = DateUtil.getTotalHoursWithMinutesAsFraction(lstReadModel.getSampleTime(), aireadModel.getSampleTime());

        //计算平均流量
        BigDecimal aveFlow = dValue.divide(new BigDecimal(dHous), new MathContext(2, RoundingMode.HALF_UP));

        GasAlarmType alarmType = GasAlarmType.UNKNOWN;
        if (aveFlow.compareTo(new BigDecimal(meterSpec.getQmax())) > 0) {//超过最大流量
            alarmType = GasAlarmType.STANDARD_ABOVE;
        } else if (aveFlow.compareTo(new BigDecimal(meterSpec.getQmin())) < 0) {//低于最小流量(*最小流量有可能判断不出来)
            alarmType = GasAlarmType.STANDARD_BELOW;
            return; //发现这个是判断不出来的,精度达不到 2024-02-07 guozhao
        } else {//正常范围
            return;
        }

        TAlarmRecordModel recordModel = new TAlarmRecordModel();
        recordModel.setStationCode(aireadModel.getStationCode());
        recordModel.setActualUsage(aveFlow);
        recordModel.setStartTime(lstReadModel.getSampleTime());
        recordModel.setEndTime(aireadModel.getSampleTime());
        recordModel.setAlarmTime(new Date());
        recordModel.setAlarmStatus(0);
        recordModel.setAlarmType(alarmType.getFlag());
        recordModel.setPlanUsage(new BigDecimal(meterSpec.getQmax()));

        alarmRecordService.saveTAlarmRecord(recordModel);
    }

    /**
     * 处理订阅报警消息
     * 根据AlarmNotice判断
     *
     * @param aireadModel
     */
    public void handleSubscribeAlarm(TAireadModel aireadModel) {
        if (mAlarmNoticeModels == null) {
            getAlarmNoticeModels();
        }
        if (mAlarmNoticeModels == null || mAlarmNoticeModels.size() == 0) return;

        //TAlarmNoticeModel alarmNoticeModel = null;
        for (TAlarmNoticeModel model : mAlarmNoticeModels) {
            if (model.getDetails() == null || model.getDetails().size() == 0) {
                continue;
            }

            //从明细中匹配遥测站码
            Optional<TAlarmNoticeDetailModel> optional =
                    model.getDetails().stream().filter(s -> s.getStationCode().equals(aireadModel.getStationCode())).findFirst();
            if (optional.isPresent()) {
                handleSubscribeRecord(aireadModel, model);
            }
        }
    }

    private void writeLog(String stationCode, String info) {
        log.warn(String.format("跳过燃气报警:[%s],%s", stationCode, info));
    }

    /**
     * 处理订阅报警记录
     *
     * @param aireadModel
     * @param alarmNoticeModel
     */
    private void handleSubscribeRecord(TAireadModel aireadModel, TAlarmNoticeModel alarmNoticeModel) {
        //匹配到了,根据间隔,查上一条读数记录,算用量
        Date sampleTime = aireadModel.getSampleTime();
        /** 下面行注释是因为,有的是非0点数据,例如在00:02 如果直接查看-24小时,会漏掉这个数 */
        //Date lstTime = DateUtil.getOtherDateByHour(sampleTime, -1 * alarmNoticeModel.getCalcInterval());
        Date lstTime = DateUtil.getOtherDateByHour(sampleTime, -1);
        TAiread lstReadModel = aireadService.findlastTAireadBydatatime(aireadModel.getStationCode(), lstTime);
        if (lstReadModel == null) {
            writeLog(aireadModel.getStationCode(), "没有符合条件的历史数据可进行比较");
            return;
        }

        double hours = DateUtil.getTotalHoursWithMinutesAsFraction(lstReadModel.getSampleTime(), sampleTime);

        //超过两个计算周期不处理 2024-03-21 guozhao
        if (hours >= alarmNoticeModel.getCalcInterval() * 2) {
            writeLog(aireadModel.getStationCode(), "两次数据间隔过长: " + hours + " 小时");
            return;
        }

        if (hours == alarmNoticeModel.getCalcInterval()) {
            //完全符合设定间隔,最大最小值均可计算(理论上允许同时报警)
            handleDosageMax(aireadModel, lstReadModel, alarmNoticeModel);
            handleDosageMin(aireadModel, lstReadModel, alarmNoticeModel);
        } else if (hours < alarmNoticeModel.getCalcInterval()) {
            //如果小于设定间隔,可粗判最大值
            handleDosageMax(aireadModel, lstReadModel, alarmNoticeModel);
        } else {
            //如何大于设定间隔,可粗判最小值
            handleDosageMin(aireadModel, lstReadModel, alarmNoticeModel);
        }
    }


    private void handleDosageMax(TAireadModel aireadModel, TAiread lstReadModel, TAlarmNoticeModel alarmNoticeModel) {
        BigDecimal value = aireadModel.getResultValue().subtract(lstReadModel.getResultValue());

        StringBuilder sb = new StringBuilder();
        sb.append("间隔[");
        sb.append(alarmNoticeModel.getCalcInterval());
        sb.append("]小时;");
        BigDecimal planUsage = BigDecimal.ZERO;
        TAlarmRecordModel recordModel = new TAlarmRecordModel();
        if (value.compareTo(new BigDecimal(alarmNoticeModel.getDosageMax())) > 0) {//高于最大值
            planUsage = new BigDecimal(alarmNoticeModel.getDosageMax());
            sb.append("设定的最大用量为:");
            sb.append(alarmNoticeModel.getDosageMax());
            sb.append("立方;");
            recordModel.setAlarmType(GasAlarmType.SUBSCRIBE_ABOVE.getFlag());
        } else {//正常范围
            return;
        }

        recordModel.setStationCode(aireadModel.getStationCode());
        recordModel.setActualUsage(value);
        recordModel.setStartTime(lstReadModel.getSampleTime());
        recordModel.setEndTime(aireadModel.getSampleTime());
        recordModel.setAlarmTime(new Date());
        recordModel.setAlarmNoticeId(alarmNoticeModel.getId());
        recordModel.setPlanUsage(planUsage);
        recordModel.setAlarmStatus(0);

        alarmRecordService.saveTAlarmRecord(recordModel);

        //发送邮件
        sb.append("实际用量为:");
        sb.append(value);
        sb.append("立方.");
        String stationName = aireadModel.getStationName();
        if (StringUtils.isEmpty(stationName)) {
            StationAccModel accModel = stationAccService.findStationAccByStationCode(aireadModel.getStationCode());
            stationName = accModel == null ? aireadModel.getStationCode() : accModel.getStationName();
        }
        String subject = "燃气用量异常提醒:" + stationName;

        notifyUtil.sendEMail(alarmNoticeModel.getEmailAddress(), subject, sb.toString());
    }

    private void handleDosageMin(TAireadModel aireadModel, TAiread lstReadModel, TAlarmNoticeModel alarmNoticeModel) {
        BigDecimal value = aireadModel.getResultValue().subtract(lstReadModel.getResultValue());

        StringBuilder sb = new StringBuilder();
        sb.append("间隔[");
        sb.append(alarmNoticeModel.getCalcInterval());
        sb.append("]小时;");
        BigDecimal planUsage = BigDecimal.ZERO;
        TAlarmRecordModel recordModel = new TAlarmRecordModel();
        if (value.compareTo(new BigDecimal(alarmNoticeModel.getDosageMin())) < 0) {//低于最小值
            planUsage = new BigDecimal(alarmNoticeModel.getDosageMin());
            sb.append("设定的最小用量为:");
            sb.append(alarmNoticeModel.getDosageMin());
            sb.append("立方;");
            recordModel.setAlarmType(GasAlarmType.SUBSCRIBE_BELOW.getFlag());
        } else {//正常范围
            return;
        }

        recordModel.setStationCode(aireadModel.getStationCode());
        recordModel.setActualUsage(value);
        recordModel.setStartTime(lstReadModel.getSampleTime());
        recordModel.setEndTime(aireadModel.getSampleTime());
        recordModel.setAlarmTime(new Date());
        recordModel.setAlarmNoticeId(alarmNoticeModel.getId());
        recordModel.setPlanUsage(planUsage);
        recordModel.setAlarmStatus(0);

        alarmRecordService.saveTAlarmRecord(recordModel);

        //发送邮件
        sb.append("实际用量为:");
        sb.append(value);
        sb.append("立方.");
        String stationName = aireadModel.getStationName();
        if (StringUtils.isEmpty(stationName)) {
            StationAccModel accModel = stationAccService.findStationAccByStationCode(aireadModel.getStationCode());
            stationName = accModel == null ? aireadModel.getStationCode() : accModel.getStationName();
        }
        String subject = "燃气用量异常提醒:" + stationName;

        notifyUtil.sendEMail(alarmNoticeModel.getEmailAddress(), subject, sb.toString());
    }

    /**
     * 获取报警通知数据列表
     */
    public void getAlarmNoticeModels() {
        TAlarmNoticeModel queryModel = new TAlarmNoticeModel();
        queryModel.setPageNum(1);
        queryModel.setPageSize(1000);
        queryModel.setUseFlag(1);
        ResPageModel pageModel = alarmNoticeService.findAllTAlarmNotice(queryModel);
        if (pageModel.getTotal() == 0) {//如果TAlarmNotice表空的情况,防止反复查询
            mAlarmNoticeModels = new ArrayList<>();
        } else {
            mAlarmNoticeModels = (List<TAlarmNoticeModel>) pageModel.getList();
        }
    }

    //由于采样时间可能非整点,采用下面最小最大间隔用来忽略分钟数
    private final int MIN_INTERVAL = 1380;//最小计算间隔分钟数
    private final int MAX_INTERVAL = 1440;//最大计算间隔分钟数

    /**
     * 处理动态区间报警(四分位)
     *
     * @param aireadModel 当前读数对象
     */
    public void handleDynamicRange(TAireadModel aireadModel) {
        TAireadModel queryModel = new TAireadModel();
        queryModel.setStationCode(aireadModel.getStationCode());
        queryModel.setPage(1);
        queryModel.setRows(45);//最近(时间太长太短都有问题,暂定45,考虑到安装时间)
        Map<String, Object> map = aireadService.findAllTAireadByCondition(queryModel);

        if (map == null || map.isEmpty() || !map.containsKey("list")) {
            return;
        }

        List<TAireadModel> list = (List<TAireadModel>) map.get("list");
        //初步过滤
        list = list.stream().filter(s -> ImageUtil.isImageByExtension(s.getBigJpgUrl()) && s.getResultValue() != null && s.getResultValue().compareTo(BigDecimal.ZERO) > 0).collect(Collectors.toList());
        //样本太少不处理
        if (list.size() < 10) {
            return;
        }
        List<BigDecimal> values = new ArrayList<>();
        BigDecimal targetValue = BigDecimal.ZERO;//昨日用量--根据今天0点-昨天0点计算出来的

        TAireadModel last = list.get(0);
        long interval = DateUtil.getMinsbetweenTime(last.getSampleTime(), aireadModel.getSampleTime());
        //来的数据和库中最新的数据间隔超过1天了,或读数无效,不处理 2024-04-08 guozhao
        if (interval > MAX_INTERVAL || interval < MIN_INTERVAL || aireadModel.getResultValue() == null || last.getResultValue() == null) {
            return;
        } else {
            if (aireadModel.getResultValue().scale() > 5) {
                aireadModel.setResultValue(aireadModel.getResultValue().setScale(5, RoundingMode.HALF_UP));
            }
            targetValue = aireadModel.getResultValue().subtract(last.getResultValue());
        }

        //boolean isCheckReview = false;//是否过滤未审核数据

        for (int i = 0; i < list.size() - 1; i++) {
            TAireadModel model1 = list.get(i);
            TAireadModel model0 = list.get(i + 1);

            //未经审核
            /*if (isCheckReview && (StringUtils.isEmpty(model1.getReviewFlag()) || !model1.getReviewFlag().equals("1"))) {
                continue;
            }*/

            //底数无效
            if (model0.getResultValue() == null || model1.getResultValue() == null) {
                continue;
            }

            BigDecimal usage = model1.getResultValue().subtract(model0.getResultValue());
            //用量<0
            if (usage.compareTo(BigDecimal.ZERO) < 0) {
                continue;
            }

            long mins = DateUtil.getMinsbetweenTime(model0.getSampleTime(), model1.getSampleTime());
            if (mins > MIN_INTERVAL && mins <= MAX_INTERVAL) {
                values.add(usage);
            }
        }

        //处理后的有效数据源太少
        if (values.size() < 10) {
            return;
        }

        QuantileModel quantileModel = NumberUtil.getQuantileResult(values);
        if (quantileModel == null) {
            return;
        }

        GasAlarmType alarmType;
        if (targetValue.compareTo(quantileModel.getLowerBound()) < 0) {//低于下限
            alarmType = GasAlarmType.DYNAMIC_BELOW;
        } else if (targetValue.compareTo(quantileModel.getUpperBound()) > 0) {//高于上限
            alarmType = GasAlarmType.DYNAMIC_ABOVE;
        } else {
            return;
        }

        TAlarmRecordModel recordModel = new TAlarmRecordModel();
        recordModel.setStationCode(aireadModel.getStationCode());
        recordModel.setActualUsage(targetValue);
        //列表超过2条用上次采样时间,否则就使用当前读数的采样时间
        recordModel.setStartTime(list.size() > 1 ? list.get(list.size() - 2).
                getSampleTime() : aireadModel.getSampleTime());
        recordModel.setEndTime(aireadModel.getSampleTime());
        recordModel.setAlarmTime(new Date());
        recordModel.setPlanUsage(alarmType == GasAlarmType.DYNAMIC_BELOW ? quantileModel.getLowerBound() : quantileModel.getUpperBound());
        recordModel.setAlarmType(alarmType.getFlag());
        recordModel.setAlarmStatus(0);
        alarmRecordService.saveTAlarmRecord(recordModel);
    }

    /**
     * 处理燃气表数据报警业务
     */
    public void handleDataMissing() {
        List<StationAccModel> accModels = stationAccService.findStationAccByUnitCode(TYMQ_UNIT_CODE);
        for (StationAccModel accModel : accModels) {
            checkDataMissing(accModel);
        }
    }

    /**
     * 校验数据丢失并生成记录
     *
     * @param accModel
     */
    public void checkDataMissing(StationAccModel accModel) {
        if (!accModel.getUnitCode().equals(TYMQ_UNIT_CODE) && !accModel.getVisUnitCodes().contains(TYMQ_UNIT_CODE)) {
            return;
        }

        TAireadModel aireadModel = aireadService.findLatesTAireadByStationCode(accModel.getStationCode());
        LocalDate nowLocal = new Date().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        GasAlarmType alarmType = GasAlarmType.UNKNOWN;
        if (aireadModel != null) {
            //最新的一条数据采样和当前时间的日期部分相同,则未丢数
            LocalDate lastLocal = aireadModel.getSampleTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            if (nowLocal.equals(lastLocal)) {
                if (!ImageUtil.isImageByExtension(aireadModel.getBigJpgUrl())) {
                    //特殊情况:在收到0点整点数据后,又收到了一条0:n分的数据,这条数据无图,不作报警,用户看不到这条数据(可能是补发或MCU程序问题导致的) 2024-04-08 guozhao
                    if (!DateUtil.formateDate(aireadModel.getSampleTime(), "mm").equals("00")) {
                        LocalDateTime localDateTime = lastLocal.atStartOfDay();
                        // 将 LocalDateTime 转换为 Date
                        Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
                        TAiread airead = aireadService.findlastTAireadBydatatime(accModel.getStationCode(), date);
                        if (airead != null && airead.getSampleTime().compareTo(date) == 0) {//是0点数据
                            //System.out.println("无图但已有0点数据了");
                            return;
                        }
                    }

                    alarmType = GasAlarmType.IMAGE_MISSING;
                }
            } else {
                alarmType = GasAlarmType.DATA_MISSING;
            }
        } else {
            //数据库中没有此站点数据(由于是5点生成的,常规情况安装也应该是前一天,没有数据判定丢失)
            alarmType = GasAlarmType.DATA_MISSING;
        }
        if (alarmType != GasAlarmType.UNKNOWN) {
            TAlarmRecordModel recordModel = new TAlarmRecordModel();
            recordModel.setStationCode(aireadModel.getStationCode());

            ZonedDateTime zonedDateTime = nowLocal.atStartOfDay(ZoneId.systemDefault());
            Date date = Date.from(zonedDateTime.toInstant());

            recordModel.setStartTime(date);
            recordModel.setEndTime(date);
            recordModel.setAlarmTime(new Date());
            recordModel.setAlarmType(alarmType.getFlag());
            recordModel.setAlarmStatus(0);
            alarmRecordService.saveTAlarmRecord(recordModel);
        }
    }
}
