package com.njcn.prepare.harmonic.service.mysql.Impl.line;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.njcn.device.biz.enums.DeviceResponseEnum;
import com.njcn.device.pms.api.DistributionMonitorClient;
import com.njcn.device.pms.api.MonitorClient;
import com.njcn.device.pms.pojo.po.DistributionMonitor;
import com.njcn.device.pms.pojo.po.Monitor;
import com.njcn.device.pq.constant.Param;
import com.njcn.device.pq.pojo.po.DataV;
import com.njcn.device.pq.pojo.po.PmsAbnormalRules;
import com.njcn.prepare.harmonic.constant.AlgorithmParam;
import com.njcn.prepare.harmonic.mapper.mysql.line.PmsAbnormalRulesMapper;
import com.njcn.prepare.harmonic.mapper.mysql.line.RStatAbnormalDMapper;
import com.njcn.prepare.harmonic.pojo.mysql.RStatAbnormalDPO;
import com.njcn.prepare.harmonic.pojo.oracle.PqLinedetailPO;
import com.njcn.prepare.harmonic.pojo.param.LineParam;
import com.njcn.prepare.harmonic.pojo.vo.DataVOracleVO;
import com.njcn.prepare.harmonic.service.influxdb.DataExceptionInfluxService;
import com.njcn.prepare.harmonic.service.mysql.line.DataExceptionService;
import com.njcn.prepare.harmonic.service.oracle.DataVOraclePOService;
import com.njcn.prepare.harmonic.service.oracle.PqLinedetailPOService;
import com.njcn.system.api.DicDataFeignClient;
import com.njcn.system.enums.DicDataTypeEnum;
import com.njcn.system.pojo.po.DictData;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * 数据是否异常
 *
 * @author qijian
 * @version 1.0.0
 * @createTime 2022/11/29 - 10:09
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class DataExceptionServiceImpl implements DataExceptionService {

    private final PmsAbnormalRulesMapper pmsAbnormalRulesMapper;

    private final RStatAbnormalDMapper rStatAbnormalDMapper;

    private final DistributionMonitorClient distributionMonitorClient;

    private final MonitorClient monitorClient;

    private final DicDataFeignClient dicDataFeignClient;

    private final DataExceptionInfluxService dataExceptionInfluxService;

    private final DataVOraclePOService dataVOraclePOService;

    private final PqLinedetailPOService pqLinedetailPOService;

    /**
     * 监测点数据是否异常（日统计）
     * @author qijian
     * @date 2022/11/29
     * @return
     */
    @Override
    @Async("asyncExecutor")
    public DeviceResponseEnum lineDataException(LineParam lineParam) {
        DeviceResponseEnum deviceResponseEnum = null;
        if (Objects.equals(lineParam.getDataSource(), AlgorithmParam.ORACLE_DATA)) {
            deviceResponseEnum = hanlderOracleData(lineParam);
        } else if (Objects.equals(lineParam.getDataSource(), AlgorithmParam.INFLUX_DATA)) {
            deviceResponseEnum = hanlderInfluxData (lineParam);
        }
        return deviceResponseEnum;
    }

    /**
     * 处理数据（influx>mysql）
     * @author qijian
     * @date 2022/11/29
     */
    private DeviceResponseEnum hanlderInfluxData(LineParam lineParam) {
        //初始化
        Date date = DateUtil.parse(lineParam.getDataDate());
        List<String> lineIds = lineParam.getLineIds();
        Integer type = lineParam.getType();
        Integer lineType = lineParam.getLineType();

        //1、取出规则
        List<PmsAbnormalRules> pmsAbnormalRules = pmsAbnormalRulesMapper.selectList(null);

        //2、取出电压字典
        List<DictData> dictDataList = dicDataFeignClient.getDicDataByTypeName(DicDataTypeEnum.DEV_VOLTAGE.getName()).getData();

        //3、获取监测点数据，进行入库操作
        if (CollUtil.isEmpty(lineIds)){
            //先获取主网的监测点信息，再获取配网的监测点信息
            List<Monitor> monitorList = monitorClient.getMonitorList(lineIds).getData();
            for (Monitor monitor : monitorList) {
                Double voltage = getVoltage(monitor.getVoltageLevel(), dictDataList);
                DeviceResponseEnum deviceResponseEnum = processInfluxData(monitor.getLineId(), voltage, date, type, pmsAbnormalRules);
                if (deviceResponseEnum != null){
                    return deviceResponseEnum;
                }
            }
            List<DistributionMonitor> distributionMonitorList = distributionMonitorClient.distributionMonitorList(lineIds).getData();
            for (DistributionMonitor distributionMonitor : distributionMonitorList) {
                Double voltage = getVoltage(distributionMonitor.getVoltageLevel(), dictDataList);
                DeviceResponseEnum deviceResponseEnum = processInfluxData(distributionMonitor.getMonitorId(), voltage, date, type, pmsAbnormalRules);
                if (deviceResponseEnum != null){
                    return deviceResponseEnum;
                }
            }
        } else {
            //根据监测点类型取数据
            if (AlgorithmParam.MAIN_NET.equals(lineType)){
                List<Monitor> monitorList = monitorClient.getMonitorList(lineIds).getData();
                for (Monitor monitor : monitorList) {
                    Double voltage = getVoltage(monitor.getVoltageLevel(), dictDataList);
                    DeviceResponseEnum deviceResponseEnum = processInfluxData(monitor.getLineId(), voltage, date, type, pmsAbnormalRules);
                    if (deviceResponseEnum != null){
                        return deviceResponseEnum;
                    }
                }
            }else{
                List<DistributionMonitor> distributionMonitorList = distributionMonitorClient.distributionMonitorList(lineIds).getData();
                for (DistributionMonitor distributionMonitor : distributionMonitorList) {
                    Double voltage = getVoltage(distributionMonitor.getVoltageLevel(), dictDataList);
                    DeviceResponseEnum deviceResponseEnum = processInfluxData(distributionMonitor.getMonitorId(), voltage, date, type, pmsAbnormalRules);
                    if (deviceResponseEnum != null){
                        return deviceResponseEnum;
                    }
                }
            }
        }

        return null;
    }

    /**
     * 处理数据（influx>mysql）
     * @author qijian
     * @date 2022/11/29
     * @return
     */
    private DeviceResponseEnum processInfluxData(String lineId, Double voltage, Date date, Integer type, List<PmsAbnormalRules> pmsAbnormalRules) {
        //取前四项进行比较（相别为A）
        DataV dataV = dataExceptionInfluxService.getFirstFourItems(lineId,date,type);
        //查不到数据为null，直接返回
        if (Objects.isNull(dataV)){
            return null;
        }

        DeviceResponseEnum harmonicResponseEnum = null;

        //根据规则表进行判断
        for (PmsAbnormalRules pmsAbnormalRule : pmsAbnormalRules) {
            //每项数据进行上下限比较（MAX和MIN），若有一项不在数据范围内，则为异常
            switch (pmsAbnormalRule.getTarget()) {
                case Param.TARGET_FREQ:
                    //频率：正常比较
                    if (dataV.getFreqMIN() < pmsAbnormalRule.getLowerLimit() || dataV.getFreqMAX() > pmsAbnormalRule.getUpperLimit()){
                        harmonicResponseEnum = DeviceResponseEnum.ALGORITHM_FREP_RULE;
                    }
                    break;
                case Param.TARGET_RMS:
                    //相电压有效值特殊处理：在【0.85p.u.，1.2p.u.】之间；p.u=电压等级/1.732
                    pmsAbnormalRule.setLowerLimit((pmsAbnormalRule.getLowerLimit() * (voltage / 1.732)));
                    pmsAbnormalRule.setUpperLimit((pmsAbnormalRule.getUpperLimit() * (voltage / 1.732)));
                    if (dataV.getRmsMIN() < pmsAbnormalRule.getLowerLimit() || dataV.getRmsMAX() > pmsAbnormalRule.getUpperLimit()){
                        harmonicResponseEnum = DeviceResponseEnum.ALGORITHM_RMS_RULE;
                    }
                    break;
                case Param.TARGET_RMS_LVR:
                    //线电压有效值特殊处理：在【0.85p.u.，1.2p.u.】之间；p.u=电压等级
                    pmsAbnormalRule.setLowerLimit((pmsAbnormalRule.getLowerLimit() * voltage));
                    pmsAbnormalRule.setUpperLimit((pmsAbnormalRule.getUpperLimit() * voltage));
                    if (dataV.getRmsLvrMIN() < pmsAbnormalRule.getLowerLimit() || dataV.getRmsLvrMAX() > pmsAbnormalRule.getUpperLimit()){
                        harmonicResponseEnum = DeviceResponseEnum.ALGORITHM_RMS_LVR_RULE;
                    }
                    break;
                case Param.TARGET_V_THD:
                    //电压总谐波畸变率：正常比较
                    if (dataV.getVThdMIN() < pmsAbnormalRule.getLowerLimit() || dataV.getVThdMAX() > pmsAbnormalRule.getUpperLimit()){
                        harmonicResponseEnum = DeviceResponseEnum.ALGORITHM_V_THD_RULE;
                    }
                    break;
                case Param.TARGET_V_UNBALANCE:
                    //三相不平衡度：正常比较（相别为T）
                    dataV = dataExceptionInfluxService.getUnbalance(lineId,date,type);
                    //InfluxDB查不到数据为null，所以直接返回
                    if (Objects.isNull(dataV)){
                        return null;
                    }
                    if (dataV.getVUnbalanceMIN() < pmsAbnormalRule.getLowerLimit() || dataV.getVUnbalanceMAX() > pmsAbnormalRule.getUpperLimit()){
                        harmonicResponseEnum = DeviceResponseEnum.ALGORITHM_V_UNBALANCE_RULE;
                    }
                    break;
                default:
                    break;
            }

            //有异常抛出并入库
            if (harmonicResponseEnum != null){
                addData(lineId, date);
                return harmonicResponseEnum;
            }

        }
        return null;
    }

    /**
     * 处理数据（oracle>mysql）
     * @author qijian
     * @date 2022/11/29
     * @return
     */
    private DeviceResponseEnum hanlderOracleData(LineParam lineParam) {
        //初始化
        Date date = DateUtil.parse(lineParam.getDataDate());
        List<String> lineIds = lineParam.getLineIds();
        Integer type = lineParam.getType();
        Integer lineType = lineParam.getLineType();

        //1、取出规则
        List<PmsAbnormalRules> pmsAbnormalRules = pmsAbnormalRulesMapper.selectList(null);

        //2、取出电压字典
        List<DictData> dictDataList = dicDataFeignClient.getDicDataByTypeName(DicDataTypeEnum.DEV_VOLTAGE.getName()).getData();

        //3、获取监测点数据，进行入库操作
        if (CollUtil.isEmpty(lineIds)){
            //先获取主网的监测点信息，再获取配网的监测点信息
            List<Monitor> monitorList = monitorClient.getMonitorList(lineIds).getData();
            for (Monitor monitor : monitorList) {
                //根据电压字典获取电压等级
                Double voltage = getVoltage(monitor.getVoltageLevel(),dictDataList);
                //根据mysql监测点取oracle监测点信息
                PqLinedetailPO pqLinedetailPO = pqLinedetailPOService.getOracleIndexIdByMonitorId(monitor.getLineId());
                //处理数据（oracle>mysql）
                DeviceResponseEnum deviceResponseEnum = processOracleData(pqLinedetailPO == null ? 0 : pqLinedetailPO.getLineIndex(), monitor.getLineId(), voltage, date, type, pmsAbnormalRules);
                if (deviceResponseEnum != null){
                    return deviceResponseEnum;
                }
            }
            List<DistributionMonitor> distributionMonitorList = distributionMonitorClient.distributionMonitorList(lineIds).getData();
            for (DistributionMonitor distributionMonitor : distributionMonitorList) {
                //根据电压字典获取电压等级
                Double voltage = getVoltage(distributionMonitor.getVoltageLevel(),dictDataList);
                //根据mysql监测点取oracle监测点信息
                PqLinedetailPO pqLinedetailPO = pqLinedetailPOService.getOracleIndexIdByMonitorId(distributionMonitor.getMonitorId());
                //处理数据（oracle>mysql）
                DeviceResponseEnum deviceResponseEnum = processOracleData(pqLinedetailPO == null ? 0 : pqLinedetailPO.getLineIndex(),distributionMonitor.getMonitorId(), voltage, date, type, pmsAbnormalRules);
                if (deviceResponseEnum != null){
                    return deviceResponseEnum;
                }
            }
        } else {
            //根据监测点类型取数据
            if (AlgorithmParam.MAIN_NET.equals(lineType)){
                List<Monitor> monitorList = monitorClient.getMonitorList(lineIds).getData();
                for (Monitor monitor : monitorList) {
                    //根据电压字典获取电压等级
                    Double voltage = getVoltage(monitor.getVoltageLevel(),dictDataList);
                    //根据mysql监测点取oracle监测点信息
                    PqLinedetailPO pqLinedetailPO = pqLinedetailPOService.getOracleIndexIdByMonitorId(monitor.getLineId());
                    //处理数据（oracle>mysql）
                    DeviceResponseEnum deviceResponseEnum = processOracleData(pqLinedetailPO == null ? 0 : pqLinedetailPO.getLineIndex(),monitor.getLineId(), voltage, date, type, pmsAbnormalRules);
                    if (deviceResponseEnum != null){
                        return deviceResponseEnum;
                    }
                }
            }else{
                List<DistributionMonitor> distributionMonitorList = distributionMonitorClient.distributionMonitorList(lineIds).getData();
                for (DistributionMonitor distributionMonitor : distributionMonitorList) {
                    //根据电压字典获取电压等级
                    Double voltage = getVoltage(distributionMonitor.getVoltageLevel(),dictDataList);
                    //根据mysql监测点取oracle监测点信息
                    PqLinedetailPO pqLinedetailPO = pqLinedetailPOService.getOracleIndexIdByMonitorId(distributionMonitor.getMonitorId());
                    //处理数据（oracle>mysql）
                    DeviceResponseEnum deviceResponseEnum = processOracleData(pqLinedetailPO == null ? 0 : pqLinedetailPO.getLineIndex(),distributionMonitor.getMonitorId(), voltage, date, type, pmsAbnormalRules);
                    if (deviceResponseEnum != null){
                        return deviceResponseEnum;
                    }
                }
            }
        }
        return null;
    }

    /**
     * 处理数据（oracle>mysql）
     * @author qijian
     * @date 2022/11/29
     */
    private DeviceResponseEnum processOracleData(Integer lineId, String monitorId, Double voltage, Date date, Integer type, List<PmsAbnormalRules> pmsAbnormalRules) {
        //取五项进行比较
        DataVOracleVO dataVOracleVO = dataVOraclePOService.getFiveItemsForDataException(lineId, date, type);
        //查不到数据为null，直接返回
        if (Objects.isNull(dataVOracleVO)){
            return null;
        }

        DeviceResponseEnum harmonicResponseEnum = null;

        //根据规则表进行判断
        for (PmsAbnormalRules pmsAbnormalRule : pmsAbnormalRules) {
            //每项数据进行上下限比较（MAX和MIN），若有一项不在数据范围内，则为异常
            switch (pmsAbnormalRule.getTarget()) {
                case Param.TARGET_FREQ:
                    //频率：正常比较
                    if (dataVOracleVO.getFreqMin() < pmsAbnormalRule.getLowerLimit() || dataVOracleVO.getFreqMax() > pmsAbnormalRule.getUpperLimit()){
                        harmonicResponseEnum = DeviceResponseEnum.ALGORITHM_FREP_RULE;
                    }
                    break;
                case Param.TARGET_RMS:
                    //相电压有效值特殊处理：在【0.85p.u.，1.2p.u.】之间；p.u=电压等级/1.732
                    pmsAbnormalRule.setLowerLimit((pmsAbnormalRule.getLowerLimit() * (voltage / 1.732)));
                    pmsAbnormalRule.setUpperLimit((pmsAbnormalRule.getUpperLimit() * (voltage / 1.732)));
                    if (dataVOracleVO.getRmsMin() < pmsAbnormalRule.getLowerLimit() || dataVOracleVO.getRmsMax() > pmsAbnormalRule.getUpperLimit()){
                        harmonicResponseEnum = DeviceResponseEnum.ALGORITHM_RMS_RULE;
                    }
                    break;
                case Param.TARGET_RMS_LVR:
                    //线电压有效值特殊处理：在【0.85p.u.，1.2p.u.】之间；p.u=电压等级
                    pmsAbnormalRule.setLowerLimit((pmsAbnormalRule.getLowerLimit() * voltage));
                    pmsAbnormalRule.setUpperLimit((pmsAbnormalRule.getUpperLimit() * voltage));
                    if (dataVOracleVO.getRmsLvrMin() < pmsAbnormalRule.getLowerLimit() || dataVOracleVO.getRmsLvrMax() > pmsAbnormalRule.getUpperLimit()){
                        harmonicResponseEnum = DeviceResponseEnum.ALGORITHM_RMS_LVR_RULE;
                    }
                    break;
                case Param.TARGET_V_THD:
                    //电压总谐波畸变率：正常比较
                    if (dataVOracleVO.getVThdMin() < pmsAbnormalRule.getLowerLimit() || dataVOracleVO.getVThdMax() > pmsAbnormalRule.getUpperLimit()){
                        harmonicResponseEnum = DeviceResponseEnum.ALGORITHM_V_THD_RULE;
                    }
                    break;
                case Param.TARGET_V_UNBALANCE:
                    //三相不平衡度：正常比较
                    if (dataVOracleVO.getVUnbalanceMin() < pmsAbnormalRule.getLowerLimit() || dataVOracleVO.getVUnbalanceMax() > pmsAbnormalRule.getUpperLimit()){
                        harmonicResponseEnum = DeviceResponseEnum.ALGORITHM_V_UNBALANCE_RULE;
                    }
                    break;
                default:
                    break;
            }

            //有异常抛出并入库
            if (harmonicResponseEnum != null){
                addData(monitorId, date);
                return harmonicResponseEnum;
            }
        }
        return null;
    }

    /**
     * 有异常抛出并入库
     * @author qijian
     * @date 2022/11/29
     */
    private void addData(String lineId, Date date) {
        LambdaQueryWrapper<RStatAbnormalDPO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(RStatAbnormalDPO::getMeasurementPointId, lineId).eq(RStatAbnormalDPO::getDataDate, date);
        RStatAbnormalDPO rStatAbnormalDOne = rStatAbnormalDMapper.selectOne(lambdaQueryWrapper);
        if (Objects.isNull(rStatAbnormalDOne)){
            RStatAbnormalDPO rStatAbnormalD = new RStatAbnormalDPO();
            rStatAbnormalD.setDataDate( date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
            rStatAbnormalD.setMeasurementPointId(lineId);
            rStatAbnormalD.setValueAlarm(0);
            rStatAbnormalDMapper.insert(rStatAbnormalD);
        }
    }


    /**
     * 根据电压字典获取电压等级
     * @author qijian
     * @date 2022/11/29
     */
    private Double getVoltage(String id, List<DictData> dictDataList){
        for (DictData dictData : dictDataList) {
            if (dictData.getId().equals(id)){
                Double result = Double.valueOf(dictData.getValue());
                return result;
            }
        }
        return null;
    }

}
