package com.njcn.product.advance.eventSource.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.common.pojo.enums.common.DataStateEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.product.advance.eventSource.mapper.RelevantLogMapper;
import com.njcn.product.advance.eventSource.mapper.RmpEventAdvanceMapper;
import com.njcn.product.advance.eventSource.pojo.dto.eventAggregate.*;
import com.njcn.product.advance.eventSource.pojo.enums.AdvanceResponseEnum;
import com.njcn.product.advance.eventSource.pojo.po.PqsRelevanceLog;
import com.njcn.product.advance.eventSource.pojo.po.RmpEventDetailAssPO;
import com.njcn.product.advance.eventSource.service.EventRelevantAnalysisService;
import com.njcn.product.advance.eventSource.service.RmpEventDetailAssService;
import com.njcn.product.advance.eventSource.utils.UtilNormalization;
import com.njcn.product.system.dict.mapper.DictDataMapper;
import com.njcn.product.system.dict.pojo.enums.DicDataEnum;
import com.njcn.product.system.dict.pojo.enums.DicDataTypeEnum;
import com.njcn.product.system.dict.pojo.po.DictData;
import com.njcn.product.terminal.mysqlTerminal.mapper.LedgerScaleMapper;
import com.njcn.product.terminal.mysqlTerminal.pojo.dto.LedgerBaseInfo;
import com.njcn.product.terminal.mysqlTerminal.pojo.po.PqsTflgployass;
import com.njcn.product.terminal.mysqlTerminal.pojo.po.RmpEventDetailPO;
import com.njcn.product.terminal.mysqlTerminal.pojo.vo.AdvanceEventDetailVO;
import com.njcn.product.terminal.mysqlTerminal.service.CommGeneralService;
import com.njcn.web.utils.RequestUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * pqs
 *
 * @author cdf
 * @date 2023/6/30
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class EventRelevantAnalysisServiceImpl extends ServiceImpl<RmpEventAdvanceMapper, RmpEventDetailPO> implements EventRelevantAnalysisService {


    private final DictDataMapper dictDataMapper;

    private final RmpEventAdvanceMapper rmpEventAdvanceMapper;

    private final RelevantLogMapper relevantLogMapper;

    private final CommGeneralService commGeneralService;

    private final RmpEventAdvanceMapper eventAdvanceMapper;

    private final LedgerScaleMapper ledgerScaleMapper;

    private final RmpEventDetailAssService rmpEventDetailAssService;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void processEvents(LocalDateTime startTime, LocalDateTime endTime, String deptId) {
        TimeInterval timeInterval = new TimeInterval();


        LocalDateTime date = LocalDateTime.now();
        HandleEvent handleEvent = new HandleEvent();
        // 获取并验证基础数据
        List<EntityGroupEvtData> baseList = handleEvent.getData(startTime, endTime, deptId);
        if (CollectionUtil.isEmpty(baseList)) {
            throw new BusinessException("当前时间段暂无可分析事件");
        }
        List<EntityGroupEvtData> otherEventList = new ArrayList<>(baseList);


        Map<String, EntityMtrans> entityMtransMap = getNodeInfo();
        Set<Map.Entry<String, EntityMtrans>> setMtrans = entityMtransMap.entrySet();
        System.out.println(setMtrans);
        //获取节点和变压器配置信息
        Map<String, Map<String, Integer>> nodeMap = getBeforeNodeInfo();
        Set<Map.Entry<String, Map<String, Integer>>> nodeSort = nodeMap.entrySet();
        System.out.println(nodeSort);
        //初始化结果容器
        List<SagEvent> listSagEvent = new ArrayList<>();
        List<RmpEventDetailAssPO> rmpEventDetailAssPoList = new ArrayList<>();

        //获取短路故障字典
        DictData dictData = dictDataMapper.getDicDataByNameAndTypeName(DicDataTypeEnum.EVENT_REASON.getName(), DicDataEnum.SHORT_TROUBLE.getName());
        for (Map.Entry<String, Map<String, Integer>> m : nodeSort) {
            List<EntityGroupEvtData> list = new ArrayList<>();
            Set<Map.Entry<String, Integer>> mapValue = m.getValue().entrySet();
            FinalData.NODE_NUM = m.getValue().size();

            for (Map.Entry<String, Integer> mm : mapValue) {
                for (EntityGroupEvtData groupEvtData : baseList) {
                    //矩阵只计算短路故障相关的事件，其他事件另行计算
                    if (groupEvtData.getNodePhysics().equals(mm.getKey()) && dictData.getId().equals(groupEvtData.getSagReason())) {
                        groupEvtData.setNode(mm.getValue());
                        list.add(groupEvtData);
                    }
                }

                // 筛选不在矩阵中的事件id
                otherEventList.removeIf(entityGroupEvtData -> entityGroupEvtData.getNodePhysics().equals(mm.getKey()) && dictData.getId().equals(entityGroupEvtData.getSagReason()));
            }

            EntityGroupEvtData[] entityGroupEvtData = new EntityGroupEvtData[list.size()];
            Collections.sort(list);
            list.toArray(entityGroupEvtData);


            for (Map.Entry<String, EntityMtrans> mEntry : setMtrans) {
                if (mEntry.getKey().equals(m.getKey())) {
                    //算法最多处理1000条数据，超过限制需分批处理 先将数据根据某种方式进行升序/降序排序，然后分段处理 加入循环处理
                    int circulation = entityGroupEvtData.length % FinalData.MAX_EVT_NUM == 0
                            ? entityGroupEvtData.length / FinalData.MAX_EVT_NUM
                            : entityGroupEvtData.length / FinalData.MAX_EVT_NUM + 1;

                    for (int i = 0; i < circulation; i++) {
                        int to = 0;

                        if (i == circulation - 1) {
                            to = entityGroupEvtData.length % FinalData.MAX_EVT_NUM > 0
                                    ? entityGroupEvtData.length
                                    : (i + 1) * FinalData.MAX_EVT_NUM - 1;
                        } else {
                            to = (i + 1) * FinalData.MAX_EVT_NUM - 1;
                        }

                        EntityGroupEvtData[] arrayObj = Arrays.copyOfRange(entityGroupEvtData,
                                i * FinalData.MAX_EVT_NUM, to);
                        EntityMtrans entityMtrans = mEntry.getValue();
                        EntityGroupData entityGroupData = handleEvent.translate(arrayObj, entityMtrans);
                        // 处理分析结果
                        handleEvent.show_group_info(entityGroupData, listSagEvent, rmpEventDetailAssPoList, date);
                    }
                }
            }
        }

        // 处理非标准数据
        disposeNonStandardData(handleEvent, otherEventList, rmpEventDetailAssPoList, listSagEvent, date);
        rmpEventDetailAssService.saveBatch(rmpEventDetailAssPoList);

        List<RmpEventDetailPO> eventUpdateList = new ArrayList<>();
        for (int i = 0; i < listSagEvent.size(); i++) {
            RmpEventDetailPO rmp = new RmpEventDetailPO();
            rmp.setEventId(listSagEvent.get(i).getIndexEventDetail());
            rmp.setEventassIndex(listSagEvent.get(i).getIndexEventAss());
            rmp.setDealTime(listSagEvent.get(i).getDealTime());
            eventUpdateList.add(rmp);
            if ((i + 1) % 1000 == 0) {
                this.updateBatchById(eventUpdateList);
                eventUpdateList.clear();
            } else if (i == listSagEvent.size() - 1) {
                this.updateBatchById(eventUpdateList);
            }
        }

        // 增加策略记录
        String describe = "用户" + RequestUtil.getLoginName() + "进行了关联分析";
        PqsRelevanceLog entityPqsRelevance = new PqsRelevanceLog();
        entityPqsRelevance.setContentDes(describe);
        entityPqsRelevance.setState(DataStateEnum.ENABLE.getCode());
        entityPqsRelevance.setTimeId(date);
        relevantLogMapper.insert(entityPqsRelevance);

        log.info("事件关联分析用时：" + timeInterval.interval() / 1000 + "秒");
    }


    /**********************************************************************
     * 归集结果与非矩阵事件进行比对
     **********************************************************************/
    public void disposeNonStandardData(HandleEvent handleEvent, List<EntityGroupEvtData> noDealList, List<RmpEventDetailAssPO> assPoList, List<SagEvent> list2, LocalDateTime date) {
        Iterator<EntityGroupEvtData> iterator = noDealList.iterator();
        while (iterator.hasNext()) {
            EntityGroupEvtData entityGroupEvtData = iterator.next();

            for (RmpEventDetailAssPO eventAssObj : assPoList) {
                long sRange = Math.abs(Duration.between(eventAssObj.getTimeId(), entityGroupEvtData.getSagEvent().getSagTime()).getSeconds());
                if (sRange < 10) {
                    int b = 0;
                    int a = 0;

                    for (SagEvent sagEvent : eventAssObj.getList()) {
                        if (sagEvent.getCata() == 9) {
                            b++;
                        } else if (sagEvent.getCata() != 10) {
                            a++;
                        }
                    }

                    if (b > 0) {
                        if (entityGroupEvtData.getCata() < 9) {
                            break;
                        }
                    } else if (a > 0) {
                        if (entityGroupEvtData.getCata() == 9) {
                            break;
                        }
                    }

                    iterator.remove();
                    entityGroupEvtData.getSagEvent().setIndexEventAss(eventAssObj.getEventAssId());
                    entityGroupEvtData.getSagEvent().setDealTime(date);
                    eventAssObj.getList().add(entityGroupEvtData.getSagEvent());
                    String describe = "事件关联分析编号" + eventAssObj.getTimeId() + "共包含" + eventAssObj.getList().size() + "个事件";
                    eventAssObj.setContentDes(describe);
                    list2.add(entityGroupEvtData.getSagEvent());
                    break;
                }
            }
        }

        // 如果还有未归集的数据则单独拎为单一事件处理
        for (EntityGroupEvtData entityGroupEvtData : noDealList) {
            String strUUID = IdUtil.simpleUUID();
            entityGroupEvtData.getSagEvent().setIndexEventAss(strUUID);
            entityGroupEvtData.getSagEvent().setDealTime(date);

            List<SagEvent> dealList = new ArrayList<>();
            dealList.add(entityGroupEvtData.getSagEvent());
            handleEvent.processing(dealList, assPoList, date);
            list2.add(entityGroupEvtData.getSagEvent());
        }
    }


    class HandleEvent {
        public EntityGroupData translate(EntityGroupEvtData[] entityGroupEvtData, EntityMtrans entityMtrans) {
            // 获取测试数据的数组长度
            int testLogNum = entityGroupEvtData.length;

            // 实例化EntityGroupData，给其中的数组分配空间
            EntityGroupData groupBuf = new EntityGroupData();

            // 填入日志
            setMatrixcata(groupBuf, entityMtrans);
            create_evt_buf(entityGroupEvtData, groupBuf, testLogNum);

            UtilNormalization.sort_Tstart(groupBuf); // 根据时标进行划分
            // 根据暂降类型进行划分
            for (int i = 0; i < groupBuf.getGrp_all_num(); i++) {
                UtilNormalization.sort_cata(groupBuf, i);
            }

            return groupBuf;
        }

        //获取原始暂降数据
        public List<EntityGroupEvtData> getData(LocalDateTime startTime, LocalDateTime endTime, String deptId) {
            List<EntityGroupEvtData> entityGroupEvtDataList = new ArrayList<>();

            List<DictData> advanceType = dictDataMapper.getDicDataByTypeCode(DicDataTypeEnum.EVENT_TYPE.getCode());
            Map<String, DictData> advanceMap = advanceType.stream().collect(Collectors.toMap(DictData::getId, Function.identity()));

            //获取时间范围内的事件
            List<AdvanceEventDetailVO> advanceEventDetailVOList = querySagEventsAll(startTime, endTime, deptId);
            for (AdvanceEventDetailVO advanceEventDetailVO : advanceEventDetailVOList) { // 获取监测点线路序号
                //母线id
                String nodePhysics = advanceEventDetailVO.getBusBarId();

                // 根据暂降类型获取高级算法对应的编号
                int cata;
                int startTimeTemp;

                if (Objects.isNull(advanceEventDetailVO.getFirstType())) {
                    cata = advanceMap.get(advanceEventDetailVO.getAdvanceType()).getAlgoDescribe();
                    long timestampMillis = advanceEventDetailVO.getStartTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                    startTimeTemp = (int) (timestampMillis / 1000);
                } else {
                    cata = advanceMap.get(advanceEventDetailVO.getAdvanceType()).getAlgoDescribe(); // 获取类型
                    long timestampMillis = advanceEventDetailVO.getFirstTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                    startTimeTemp = (int) (timestampMillis / 1000);
                }

                // 填充SagEvent对象数据
                SagEvent sagEvent = new SagEvent();

                sagEvent.setIndexEventDetail(advanceEventDetailVO.getEventId());
                sagEvent.setSagTime(advanceEventDetailVO.getStartTime());
                sagEvent.setFirstTime(advanceEventDetailVO.getFirstTime());// 必须增加，否则序列化出错
                sagEvent.setTime(Timestamp.valueOf(advanceEventDetailVO.getStartTime()).getTime());
                sagEvent.setFirstTimeMills((long) startTimeTemp);
                sagEvent.setMsec(advanceEventDetailVO.getDuration());
                PlantInfo plantInfo = new PlantInfo();
                plantInfo.setNameBD(advanceEventDetailVO.getSubName());
                plantInfo.setNameGD(advanceEventDetailVO.getGdName());
                plantInfo.setNamePoint(advanceEventDetailVO.getLineId());
                sagEvent.setPlantInfo(plantInfo);
                sagEvent.setIndexPoint(advanceEventDetailVO.getLineId());
                sagEvent.setCata(cata);


                EntityGroupEvtData entityGroupEvtData = new EntityGroupEvtData(nodePhysics, startTimeTemp, cata, -1, sagEvent, advanceEventDetailVO.getAdvanceReason());
                entityGroupEvtDataList.add(entityGroupEvtData);
            }

            return entityGroupEvtDataList;
        }


        public void create_evt_buf(EntityGroupEvtData[] arr, EntityGroupData obj, int len) {
            System.arraycopy(arr, 0, obj.getIn_buf(), 0, arr.length);
            obj.setEvt_in_num(len);
        }

        public void create_matrixcata(List<EntityLogic> list, EntityMtrans entityMtrans) {
            EntityLogic[] node_data = new EntityLogic[list.size()];

            for (int i = 0; i < list.size(); i++) {
                node_data[i] = list.get(i);
            }

            int len = node_data.length;

            UtilNormalization.matrixcata_pro(node_data, entityMtrans, len);
        }

        public void setMatrixcata(EntityGroupData obj, EntityMtrans entityMtrans) {
            int i, j;
            for (i = 0; i < (FinalData.MAX_CATA_NUM - 1); i++) {
                for (j = 0; j < FinalData.NODE_NUM; j++) {
                    obj.getMatrixcata()[i][j] = entityMtrans.getMatrixcata1()[i][j];
                }
            }
        }

        public void show_group_info(EntityGroupData obj, List<SagEvent> list, List<RmpEventDetailAssPO> assEvent, LocalDateTime date) {
            int i, j, k;
            for (i = 0; i < obj.getGrp_all_num(); i++) {
                String strUUID = IdUtil.simpleUUID();
                List<SagEvent> listTem = new ArrayList<>();

                for (j = 0; j < FinalData.MAX_CATA_NUM + 2; j++) {
                    if (obj.getGrp_cata_num()[i][j] != 0) {
                        for (k = 0; k < obj.getGrp_cata_num()[i][j]; k++) {
                            obj.getGrp_cata_buf()[i][j][k].getSagEvent().setIndexEventAss(strUUID);
                            obj.getGrp_cata_buf()[i][j][k].getSagEvent().setDealTime(date);
                            listTem.add(obj.getGrp_cata_buf()[i][j][k].getSagEvent());
                            list.add(obj.getGrp_cata_buf()[i][j][k].getSagEvent());
                        }
                    }
                }

                if (!listTem.isEmpty()) {
                    processing(listTem, assEvent, date);
                }
            }
        }

        public void processing(List<SagEvent> list, List<RmpEventDetailAssPO> lists, LocalDateTime date) {
            // 根据暂降事件发生时间进行排序
            Collections.sort(list);
            RmpEventDetailAssPO eventAssObj = new RmpEventDetailAssPO();
            String strUUID = list.get(0).getIndexEventAss();

            // 归一化处理数据填充
            eventAssObj.setEventAssId(strUUID);

            // 事件发生时间
            eventAssObj.setTimeId(list.get(0).getSagTime());


            // 获取当前用户GUID
            eventAssObj.setCreateBy(RequestUtil.getUserId());

            // 是否进行范围分析 默认未分析
            eventAssObj.setAnalyseFlag(1);

            // 更新时间
            eventAssObj.setUpdateTime(date);


            // 暂降事件描述
            String codeName = LocalDateTimeUtil.format(eventAssObj.getTimeId(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
            String describe = "事件关联分析编号" + codeName + "共包含" + list.size() + "个事件";
            eventAssObj.setContentDes(describe);
            eventAssObj.setList(list);
            lists.add(eventAssObj);
        }
    }


    Map<String, Map<String, Integer>> getBeforeNodeInfo() {
        Map<String, Map<String, Integer>> setNodeSort = new HashMap<>();
        List<EntityLogic> list = rmpEventAdvanceMapper.getLogic();

        if (CollectionUtil.isNotEmpty(list)) {
            Map<String, List<String>> map = getLogicInfo(list);
            setNodeSort = nodeSort(map);
        }
        return setNodeSort;
    }


    /*************************************************************************************
     * 获取变压器信息并生成矩阵
     *************************************************************************************/
    public Map<String, EntityMtrans> getNodeInfo() {
        Map<String, EntityMtrans> entityMtranMap = new HashMap<>(32);
        List<EntityLogic> list = rmpEventAdvanceMapper.getLogic();

        if (CollectionUtil.isNotEmpty(list)) {
            Map<String, List<String>> map = getLogicInfo(list);
            Map<String, Map<String, Integer>> setNodeSort = nodeSort(map);

            setNodeSort.forEach((key, val) -> {
                FinalData.NODE_NUM = val.size();
                List<EntityLogic> listNew = new ArrayList<>();

                for (EntityLogic entityLogic : list) {
                    if (entityLogic.getTPIndex().equals(key)) {
                        entityLogic.setNode_h(val.get(entityLogic.getNodeBefore()));
                        entityLogic.setNode_l(val.get(entityLogic.getNodeNext()));
                        listNew.add(entityLogic);
                    }
                }

                EntityMtrans entityMtrans = new EntityMtrans();

                HandleEvent handleEvent = new HandleEvent();
                handleEvent.create_matrixcata(listNew, entityMtrans);
                entityMtranMap.put(key, entityMtrans);
            });
        }
        return entityMtranMap;
    }


    /*******************************************
     * 增加排序功能并缓存进redis
     *******************************************/
    public Map<String, Map<String, Integer>> nodeSort(Map<String, List<String>> mapList) {
        Set<Map.Entry<String, List<String>>> sets = mapList.entrySet();
        Map<String, Map<String, Integer>> map = new HashMap<>();

        for (Map.Entry<String, List<String>> m : sets) {
            int index = 1;
            Map<String, Integer> map2 = new HashMap<>();

            for (String item : m.getValue()) {
                map2.put(item, index++);
            }

            map.put(m.getKey(), map2);
        }
        return map;
    }


    /**
     * 抽取物理隔绝信息与母线的关系并放入map集合中
     * 与getTflgPloyInfo()方法功能类似
     */
    public Map<String, List<String>> getLogicInfo(List<EntityLogic> list) {
        if (list.size() > 0) {
            Iterator<String> iterator = getAreaInfo(list).iterator();
            Map<String, List<String>> map = new HashMap<>();

            while (iterator.hasNext()) {
                List<String> listLogic = new ArrayList<>();
                String areaString = iterator.next();

                for (EntityLogic entityLogic : list) {
                    if (entityLogic.getTPIndex().equals(areaString)) {
                        listLogic.add(entityLogic.getNodeBefore());
                        listLogic.add(entityLogic.getNodeNext());
                    }
                }

                //去除list中重复数据
                Set<String> set = new TreeSet<>(listLogic);
                map.put(areaString, new ArrayList<>(set));
            }

            return map;
        }

        return null;
    }


    /**
     * 获取物理隔绝编码信息
     * 供getInfo()、getLogicInfo()方法使用
     * 先从list数组中去重，然后获取物理隔绝编码
     */
    public <T> Set<String> getAreaInfo(List<T> list) {
        Set<T> set = new HashSet<T>(list);
        Iterator<T> iterator = set.iterator();
        Set<String> setReturn = new HashSet<String>();

        while (iterator.hasNext()) {
            Object object = iterator.next();

            if (object instanceof PqsTflgployass) {
                setReturn.add(((PqsTflgployass) object).getTpIndex());
                continue;
            }

            setReturn.add(((EntityLogic) object).getTPIndex());
        }

        return setReturn;
    }


    public List<AdvanceEventDetailVO> querySagEventsAll(LocalDateTime startTime, LocalDateTime endTime, String deptId) {
        List<AdvanceEventDetailVO> result = new ArrayList<>();
        List<String> lineIds = commGeneralService.getRunLineIdsByDept(deptId);
        if (CollUtil.isNotEmpty(lineIds)) {

            LambdaQueryWrapper<RmpEventDetailPO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.and(i -> i.isNull(RmpEventDetailPO::getEventassIndex).or().eq(RmpEventDetailPO::getEventassIndex, ""))
                    .between(RmpEventDetailPO::getStartTime, startTime, endTime).in(RmpEventDetailPO::getMeasurementPointId,lineIds);
            List<RmpEventDetailPO> rmpEventDetailPOList = eventAdvanceMapper.selectList(lambdaQueryWrapper);
            if (CollectionUtil.isEmpty(rmpEventDetailPOList)) {
                throw new BusinessException(AdvanceResponseEnum.EVENT_EMPTY);
            }
            List<String> tempLineIds = rmpEventDetailPOList.stream().map(RmpEventDetailPO::getLineId).distinct().collect(Collectors.toList());
            List<LedgerBaseInfo> temLine = ledgerScaleMapper.getLedgerBaseInfo(tempLineIds);
            Map<String, LedgerBaseInfo> map = temLine.stream().collect(Collectors.toMap(LedgerBaseInfo::getLineId, Function.identity()));

            result = BeanUtil.copyToList(rmpEventDetailPOList, AdvanceEventDetailVO.class);
            result.forEach(item -> {
                if (map.containsKey(item.getLineId())) {
                    LedgerBaseInfo areaLineInfoVO = map.get(item.getLineId());
                    item.setGdName(areaLineInfoVO.getGdName());
                    item.setSubName(areaLineInfoVO.getStationName());
                    item.setNum(areaLineInfoVO.getNum());
                    item.setVoltageId(areaLineInfoVO.getVoltageLevel());
                    item.setBusBarId(areaLineInfoVO.getBusBarId());
                }
            });
        }
        result = result.stream().filter(it -> StrUtil.isNotBlank(it.getBusBarId())).collect(Collectors.toList());
        return result;
    }
}

