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

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.github.jeffreyning.mybatisplus.service.MppServiceImpl;
import com.njcn.common.pojo.enums.common.DataStateEnum;
import com.njcn.device.pq.pojo.po.PqsTopMsg;
import com.njcn.influx.deprecated.InfluxDBPublicParam;
import com.njcn.influx.pojo.po.PqsCommunicate;
import com.njcn.influx.utils.InfluxDbUtils;
import com.njcn.prepare.harmonic.mapper.mysql.device.DeviceAbnormaStatisticsMapper;
import com.njcn.prepare.harmonic.pojo.bo.CalculatedParam;
import com.njcn.prepare.harmonic.pojo.mysql.po.line.AlarmPO;
import com.njcn.prepare.harmonic.pojo.mysql.po.line.AlarmStrategyVO;
import com.njcn.prepare.harmonic.bo.TopMsgPO;
import com.njcn.prepare.harmonic.pojo.param.DeviceAbnormaStatisticsParam;
import com.njcn.prepare.harmonic.service.mysql.device.DeviceAbnormalStatisticsService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.influxdb.dto.QueryResult;
import org.influxdb.impl.InfluxDBResultMapper;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

/**
 *
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class DeviceAbnormaStatisticsServiceImpl extends MppServiceImpl<DeviceAbnormaStatisticsMapper, PqsTopMsg> implements DeviceAbnormalStatisticsService {

    private final DeviceAbnormaStatisticsMapper deviceAbnormaStatisticsMapper;

    private final InfluxDbUtils influxDbUtils;

    @Override
    public void deviceAbnormaStatisticsD(CalculatedParam calculatedParam) {
        Map<String, List<TopMsgPO>> comMap = new HashMap<>();
        Map<String, List<AlarmPO>> deviceFlowMap  = new HashMap<>();
        // 获取所有装置以及下属监测点
        List<AlarmPO> lines = deviceAbnormaStatisticsMapper.getLinesById(calculatedParam.getIdList());
        if (CollectionUtil.isNotEmpty(lines)){
            // 根据装置id分组
            Map<String, List<AlarmPO>> devMap = lines.stream().collect(Collectors.groupingBy(AlarmPO::getDeviceId));
            // 监测点集合
            List<String> monotorIds = lines.stream().map(AlarmPO::getId).collect(Collectors.toList());
            // 装置id集合
            List<String> devIds = new ArrayList<>(devMap.keySet());
            // 获取异常告警策略
            List<AlarmStrategyVO> alarmStrategyVOS = deviceAbnormaStatisticsMapper.selectAlarmStrategy();
            Map<Integer, List<AlarmStrategyVO>> levMap = alarmStrategyVOS.stream().collect(Collectors.groupingBy(AlarmStrategyVO::getAlgoDesc));
            // 获取监测点数据完整性
            List<AlarmPO> lineIntegritys = deviceAbnormaStatisticsMapper.getIntegrityByLineIds(
                    monotorIds,
                    DateUtil.beginOfDay(DateUtil.parse(calculatedParam.getDataDate())),
                    DateUtil.endOfDay(DateUtil.parse(calculatedParam.getDataDate())));
            Map<String, List<AlarmPO>> lineIntegrityMap = lineIntegritys.stream().collect(Collectors.groupingBy(AlarmPO::getDeviceId));
            // 告警信息统计
            List<AlarmPO> lineAlarms = deviceAbnormaStatisticsMapper.getAlarmByLines(
                    monotorIds,
                    devIds,
                    DateUtil.beginOfDay(DateUtil.parse(calculatedParam.getDataDate())),
                    DateUtil.endOfDay(DateUtil.parse(calculatedParam.getDataDate())));
            Map<String, List<AlarmPO>> lineAlarmsMap = lineAlarms.stream().collect(Collectors.groupingBy(AlarmPO::getDeviceId));
            // 获取终端在线率
            List<AlarmPO> deviceOnlineRate = deviceAbnormaStatisticsMapper.getOnlineRateByDevice(
                    devIds,
                    DateUtil.beginOfDay(DateUtil.parse(calculatedParam.getDataDate())),
                    DateUtil.endOfDay(DateUtil.parse(calculatedParam.getDataDate())));
            Map<String, List<AlarmPO>> deviceOnlineMap = deviceOnlineRate.stream().collect(Collectors.groupingBy(AlarmPO::getDeviceId));
            // 获取设备流量
            List<AlarmPO> deviceFlow = deviceAbnormaStatisticsMapper.getFlowByDevice(devIds,calculatedParam.getDataDate().substring(0,7));
            if (CollectionUtil.isNotEmpty(deviceFlow)) {
                deviceFlowMap = deviceFlow.stream().collect(Collectors.groupingBy(AlarmPO::getDeviceId));
            }
            // 获取通信中断信息
            List<TopMsgPO> comMsgs = getPqsCommunicate(devIds, calculatedParam.getDataDate());
            if (CollectionUtil.isNotEmpty(comMsgs)) {
                comMap = comMsgs.stream().collect(Collectors.groupingBy(TopMsgPO::getDevId));
            }
            // 整合装置信息
            List<PqsTopMsg> topMsgPOS = new ArrayList<>();
            for (Map.Entry<String, List<AlarmPO>> devEntry : devMap.entrySet()) {
                PqsTopMsg topMsg = new PqsTopMsg();
                topMsg.setDevId(devEntry.getKey());
                topMsg.setTimeId(LocalDate.parse(calculatedParam.getDataDate(), DatePattern.NORM_DATE_FORMATTER));
                topMsg.setState(DataStateEnum.ENABLE.getCode());
                // 等级对应的告警策略
                int deviceLevel = devEntry.getValue().stream().mapToInt(AlarmPO::getLevel).min().orElse(-1);
                AlarmStrategyVO alarmStrategyVO = levMap.get(deviceLevel).get(0);
                // 数据完整性
                if (lineIntegrityMap.containsKey(devEntry.getKey())) {
                    topMsg.setIntegrityFlag(lineIntegrityMap.get(devEntry.getKey()).get(0).getIntegrity() < alarmStrategyVO.getIntegrityValue() ? 0 : 1);
                    topMsg.setIntegrityValue(lineIntegrityMap.get(devEntry.getKey()).get(0).getIntegrity());
                } else {
                    topMsg.setIntegrityFlag(-1);
                }
                // 装置告警
                if (lineAlarmsMap.containsKey(devEntry.getKey())) {
                    List<AlarmPO> lineAlarmPOS = lineAlarmsMap.get(devEntry.getKey());
                    int alarmCount = lineAlarmPOS.stream().mapToInt(AlarmPO::getAlarmCount).sum();
                    String alarmDesc = String.join(",", lineAlarmPOS.stream().map(AlarmPO::getAlarmDesc).collect(Collectors.toList()));
                    topMsg.setAlarmCount(alarmCount);
                    topMsg.setAlarmDesc(alarmDesc);
                } else {
                    topMsg.setAlarmCount(0);
                }
                // 在线率
                if (deviceOnlineMap.containsKey(devEntry.getKey())) {
                    AlarmPO alarmPO = deviceOnlineMap.get(devEntry.getKey()).get(0);
                    topMsg.setOnlinerateFlag(alarmPO.getOnlineRate() * 100 < alarmStrategyVO.getOnlineValue() ? 0 : 1);
                    topMsg.setOnlinerateValue(alarmPO.getOnlineRate() *100);
                } else {
                    topMsg.setOnlinerateFlag(-1);
                }
                // 流量
                if (deviceFlowMap.containsKey(devEntry.getKey()) && Objects.nonNull(deviceFlowMap.get(devEntry.getKey()).get(0).getFlowProportion())) {
                    AlarmPO alarmPO = deviceFlowMap.get(devEntry.getKey()).get(0);
                    topMsg.setFlowFlag(alarmPO.getActualValue() > alarmPO.getFlowMeal() ? 0 : 1);
                    topMsg.setFlowValue(alarmPO.getFlowProportion());
                } else {
                    topMsg.setFlowFlag(-1);
                }

                if (comMap.containsKey(devEntry.getKey())) {
                    TopMsgPO comOutPO = comMap.get(devEntry.getKey()).get(0);
                    List<PqsCommunicate> abnormals = comOutPO.getCommunicates().stream().filter(t -> t.getDuration() > alarmStrategyVO.getOffTimeValue()).collect(Collectors.toList());
                    topMsg.setComoutCount(abnormals.size());
                    List<String> abnormalDesc = abnormals.stream().map(PqsCommunicate::getDesc).collect(Collectors.toList());
                    topMsg.setComoutDesc(String.join(",",abnormalDesc));
                } else {
                    topMsg.setComoutCount(0);
                }
                if (topMsg.getFlowFlag() ==0 || topMsg.getIntegrityFlag() ==0 || topMsg.getOnlinerateFlag() ==0) {
                    topMsgPOS.add(topMsg);
                }
            }
            if (CollectionUtil.isNotEmpty(topMsgPOS)){
                this.saveOrUpdateBatchByMultiId(topMsgPOS);
            }
        }
    }

    public List<TopMsgPO> getPqsCommunicate(List<String> devs, String time) {
        // 组装sql语句
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("time >= '").append(DateUtil.beginOfDay(DateUtil.parse(time))).append("' and ").append("time <= '").append(DateUtil.endOfDay(DateUtil.parse(time))).append("' and ");
        for (int i = 0; i < devs.size(); i++) {
            if (devs.size() - i != 1) {
                stringBuilder.append("dev_id ='").append(devs.get(i)).append("' or ");
            } else {
                stringBuilder.append("dev_id ='").append(devs.get(i)).append("' ");
            }
        }
        String sql = "select * from " + InfluxDBPublicParam.PQS_COMMUNICATE + "  where " + stringBuilder.toString() + InfluxDBPublicParam.TIME_ZONE;
        // 获取暂降事件
        QueryResult result = influxDbUtils.query(sql);
        InfluxDBResultMapper influxDBResultMapper = new InfluxDBResultMapper();
        List<PqsCommunicate> communicates = influxDBResultMapper.toPOJO(result, PqsCommunicate.class);
        // 通讯状态统计结果集
        List<TopMsgPO> comMsgs = new ArrayList<>();
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        Map<String, List<PqsCommunicate>> comMap = communicates.stream().collect(Collectors.groupingBy(PqsCommunicate::getDevId));
        if (CollectionUtil.isNotEmpty(comMap)) {
            for (Map.Entry<String, List<PqsCommunicate>> entry : comMap.entrySet()) {
                String key = entry.getKey();
                List<PqsCommunicate> value = entry.getValue();
                TopMsgPO topMsgPO = new TopMsgPO();
                List<PqsCommunicate> comOuts  = new ArrayList<>();
                topMsgPO.setDevId(key);
                // 根据日期排序
                List<PqsCommunicate> sortedList = value.stream().sorted(Comparator.comparing(PqsCommunicate::getTime)).collect(Collectors.toList());
                if (sortedList.get(0).getType() == 1) {
                    PqsCommunicate communicate = new PqsCommunicate();
                    int durationTime = (int) (Date.from(sortedList.get(0).getTime()).getTime() - DateUtil.beginOfDay(DateUtil.parse(time)).getTime())/1000/60;
                    communicate.setDuration(durationTime);
                    communicate.setDesc((DateUtil.beginOfDay(DateUtil.parse(time))) + "至" + formatter.format(Date.from(sortedList.get(0).getTime())));
                    comOuts.add(communicate);
                }
                for (int i = 0; i < sortedList.size(); i++) {
                    if (sortedList.get(i).getType() == 0 && i < sortedList.size() - 1) {
                        PqsCommunicate communicate = new PqsCommunicate();
                        int durationTime = (int) (Date.from(sortedList.get(i + 1).getTime()).getTime() - Date.from(sortedList.get(i).getTime()).getTime())/1000/60;
                        communicate.setDuration(durationTime);
                        String comOut = formatter.format(Date.from(sortedList.get(i).getTime())) + "至" + formatter.format(Date.from(sortedList.get(i + 1).getTime()));
                        communicate.setDesc(comOut);
                        comOuts.add(communicate);
                    } else if (sortedList.get(i).getType() == 0 && i == sortedList.size() - 1) {
                        PqsCommunicate communicate = new PqsCommunicate();
                        int durationTime = (int) (DateUtil.endOfDay(DateUtil.parse(time)).getTime() - Date.from(sortedList.get(i).getTime()).getTime()) / 1000 / 60;
                        communicate.setDuration(durationTime);
                        String comOut = formatter.format(Date.from(sortedList.get(i).getTime())) + "至" + DateUtil.endOfDay(DateUtil.parse(time));
                        communicate.setDesc(comOut);
                        comOuts.add(communicate);
                    }
                }
                topMsgPO.setCommunicates(comOuts);
                comMsgs.add(topMsgPO);
            }
        }

        List<String> coms = new ArrayList<>(comMap.keySet());
        // 计算influxdb中当天无记录装置id
        List<String> extraComs = devs.stream().filter(t -> !coms.contains(t)).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(extraComs)) {
            for (String devId : extraComs) {
                TopMsgPO topMsgPO = new TopMsgPO();
                List<PqsCommunicate> comOuts  = new ArrayList<>();
                topMsgPO.setDevId(devId);
                // 组装sql语句
                StringBuilder builder = new StringBuilder();
                builder.append("dev_id ='").append(devId).append("'");
                builder.append(" AND time <= '").append(DateUtil.beginOfDay(DateUtil.parse(time))).append("'").append(" order by time DESC ").append("limit 1 ");
                String sql1 = "select * from " + InfluxDBPublicParam.PQS_COMMUNICATE + "  where "+builder.toString() + InfluxDBPublicParam.TIME_ZONE;
                // 获取暂降事件
                QueryResult result1 = influxDbUtils.query(sql1);
                InfluxDBResultMapper influxDBResultMapper1 = new InfluxDBResultMapper();
                List<PqsCommunicate> earlyData = influxDBResultMapper1.toPOJO(result1, PqsCommunicate.class);
                if (earlyData.size()==0){
                    continue;
                }
                PqsCommunicate early = earlyData.get(0);
                PqsCommunicate communicate = new PqsCommunicate();
                if (early.getType() == 0) {
                    int durationTime = (int) (DateUtil.endOfDay(DateUtil.parse(time)).getTime() - Date.from(early.getTime()).getTime()) / 1000 / 60;
                    communicate.setDuration(durationTime);
                    communicate.setDesc(formatter.format(Date.from(early.getTime())) + "至" + DateUtil.endOfDay(DateUtil.parse(time)));
                    comOuts.add(communicate);
                }
                topMsgPO.setCommunicates(comOuts);
                comMsgs.add(topMsgPO);
            }
        }
        return comMsgs;
    }
}
