package com.njcn.advance.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.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.advance.enums.AdvanceResponseEnum;
import com.njcn.advance.mapper.RelevantMapper;
import com.njcn.advance.mapper.RmpEventAdvanceMapper;
import com.njcn.advance.mapper.RmpEventDetailAssMapper;
import com.njcn.advance.pojo.dto.SagEvent;
import com.njcn.advance.pojo.dto.relevent.*;
import com.njcn.advance.pojo.po.PqsRelevanceLog;
import com.njcn.advance.service.EventRelevantAnalysisService;

import com.njcn.advance.utils.UtilNormalization;
import com.njcn.common.pojo.enums.common.DataStateEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.common.utils.PubUtils;
import com.njcn.device.pq.api.GeneralDeviceInfoClient;
import com.njcn.device.pq.api.LineFeignClient;
import com.njcn.device.pq.pojo.vo.AreaLineInfoVO;
import com.njcn.event.api.RmpEventDetailFeignClient;
import com.njcn.event.pojo.po.RmpEventDetailPO;
import com.njcn.event.pojo.vo.AdvanceEventDetailVO;
import com.njcn.system.api.DicDataFeignClient;
import com.njcn.system.enums.DicDataEnum;
import com.njcn.system.enums.DicDataTypeEnum;
import com.njcn.system.enums.SystemResponseEnum;
import com.njcn.system.pojo.po.DictData;
import com.njcn.web.pojo.param.BaseParam;
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 org.springframework.web.bind.annotation.RequestParam;

import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
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 RmpEventDetailFeignClient rmpEventDetailFeignClient;

    private final DicDataFeignClient dicDataFeignClient;

    private final RelevantMapper relevantMapper;

    private final GeneralDeviceInfoClient generalDeviceInfoClient;

    private final RmpEventAdvanceMapper eventAdvanceMapper;

    private final LineFeignClient lineFeignClient;

    private final RmpEventDetailAssMapper rmpEventDetailAssMapper;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void processEvents(LocalDateTime startTime,LocalDateTime endTime) {

        TimeInterval timeInterval = new TimeInterval();

        DictData dictData = dicDataFeignClient.getDicDataByNameAndTypeName(DicDataTypeEnum.EVENT_REASON.getName(), DicDataEnum.SHORT_TROUBLE.getName()).getData();
        if (Objects.isNull(dictData)) {
            throw new BusinessException(SystemResponseEnum.ADVANCE_REASON);
        }


        LocalDateTime date = LocalDateTime.now();
        HandleEvent handleEvent = new HandleEvent();
        // 分析的事件进行处理
        List<EntityGroupEvtData> baseList = handleEvent.getData(generalDeviceInfoClient,startTime,endTime);

        // 传入的处理事件根据物理隔绝进行分组

        List<EntityLogic> strategyList = relevantMapper.getLogic();

        if (CollectionUtil.isNotEmpty(strategyList)) {
            List<SagEvent> listSagEvent = new ArrayList<>();
            List<EventAssObj> listEventAssObj = new ArrayList<>();

            Map<String, List<String>> strategyToBusBarMap = new HashMap<>();

            Map<String, EntityMtrans> mapRedis = new HashMap<>();

            Map<String, List<EntityLogic>> strategyMap = strategyList.stream().collect(Collectors.groupingBy(EntityLogic::getTPIndex));
            strategyMap.forEach((key, list) -> {
                List<String> before = list.stream().map(EntityLogic::getNodeBefore).distinct().collect(Collectors.toList());
                List<String> after = list.stream().map(EntityLogic::getNodeNext).distinct().collect(Collectors.toList());
                before.addAll(after);
                before = before.stream().distinct().collect(Collectors.toList());
                strategyToBusBarMap.put(key, before);

                FinalData.NODE_NUM = before.size();
                EntityMtrans entityMtrans = new EntityMtrans();
                handleEvent.create_matrixcata(list, entityMtrans);

                mapRedis.put(key, entityMtrans);
            });
            //TODO 是否要处理排序

            strategyToBusBarMap.forEach((lastKey, lastVal) -> {
                int index = 1;
                List<EntityGroupEvtData> list = new ArrayList<EntityGroupEvtData>();
                for (EntityGroupEvtData entityGroupEvtData : baseList) {
                    if (lastVal.contains(entityGroupEvtData.getNodePhysics()) && dictData.getId().equals(entityGroupEvtData.getSagReason())) {
                        entityGroupEvtData.setNode(index++);
                        list.add(entityGroupEvtData);
                    }
                }

                baseList.removeIf(entityGroupEvtData -> lastVal.contains(entityGroupEvtData.getNodePhysics()) && dictData.getId().equals(entityGroupEvtData.getSagReason()));


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

                mapRedis.forEach((mKey, mVal) -> {
                    if (mKey.equals(lastKey)) {
                        /********************************************************************
                         * 算法最多处理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);
                            EntityGroupData entityGroupData = handleEvent.translate(arrayObj, mVal);
                            // 处理分析结果
                            handleEvent.show_group_info(entityGroupData, listSagEvent, listEventAssObj, date);
                        }
                    }
                });

            });


            /******************************************************************************
             * 事件ID不在矩阵中，结果集为基础以时标为标准进行归集处理 注意：三相与(单相/两相)互斥
             *****************************************************************************/
            disposeNonStandardData(handleEvent, baseList, listEventAssObj, listSagEvent, date);


            int listSize = listEventAssObj.size();
            int toIndex = 1000;
            for (int i = 0; i < listSize; i += 1000) {
                //作用为toIndex最后没有toIndex条数据则剩余几条newList中就装几条
                if (i + 1000 > listSize) {
                    toIndex = listSize - i;
                }
                //分割lst
                List<EventAssObj> newList = listEventAssObj.subList(i, i + toIndex);
                //写入添加方法,需要写你的新增方法，把newList分切后的数据新增进入数据库。
                rmpEventDetailAssMapper.insertEventAssData(newList);
            }

            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.getUserNickname() + "进行了关联分析";
            PqsRelevanceLog entityPqsRelevance = new PqsRelevanceLog();
            entityPqsRelevance.setContentDes(describe);
            entityPqsRelevance.setState(DataStateEnum.ENABLE.getCode());
            entityPqsRelevance.setTimeId(date);
            relevantMapper.insert(entityPqsRelevance);

        }

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

    @Override
    public Page<AdvanceEventDetailVO> querySagEventsPage(BaseParam baseParam) {
        return null;
    }


    /**********************************************************************
     * 归集结果与非矩阵事件进行比对
     **********************************************************************/
    public void disposeNonStandardData(HandleEvent handleEvent, List<EntityGroupEvtData> noDealList, List<EventAssObj> list, List<SagEvent> list2, LocalDateTime date) {
        // 如果还有未归集的数据则单独拎为单一事件处理
        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, list, date);
            list2.add(entityGroupEvtData.getSagEvent());
        }
    }


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

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

            // 填入日志
            setMatrixcata(group_buf, entityMtrans);
            create_evt_buf(entityGroupEvtData, group_buf, test_log_num);

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

            return group_buf;
        }

        public List<EntityGroupEvtData> getData(GeneralDeviceInfoClient generalDeviceInfoClient,LocalDateTime startTime,LocalDateTime endTime) {
            List<EntityGroupEvtData> entityGroupEvtDataList = new ArrayList<>();

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

            List<AdvanceEventDetailVO> advanceEventDetailVOLsit = querySagEventsAll(startTime,endTime);


            for (int i = 0; i < advanceEventDetailVOLsit.size(); i++) { // 获取监测点线路序号
                //母线id
                String nodePhysics = advanceEventDetailVOLsit.get(i).getVoltageId();

                // 根据暂降类型获取高级算法对应的编号
                int cata = 0;
                long ll = 0L;
                int start_time = 0;

                if (Objects.isNull(advanceEventDetailVOLsit.get(i).getFirstType())) {
                    cata = advanceMap.get(advanceEventDetailVOLsit.get(i).getAdvanceType()).getAlgoDescribe();
                    ll = (long) (Timestamp.valueOf(advanceEventDetailVOLsit.get(i).getStartTime()).getTime()
                            + (advanceEventDetailVOLsit.get(i).getDuration() * 1000));
                    start_time = (int) (ll / 1000);
                } else {
                    cata = advanceMap.get(advanceEventDetailVOLsit.get(i).getAdvanceType()).getAlgoDescribe(); // 获取类型
                    ll = (long) (Timestamp.valueOf(advanceEventDetailVOLsit.get(i).getFirstTime()).getTime()
                            + (advanceEventDetailVOLsit.get(i).getFirstMs()));
                    start_time = (int) (ll / 1000);
                }

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

                sagEvent.setIndexEventDetail(advanceEventDetailVOLsit.get(i).getEventId());
                sagEvent.setSagTime(advanceEventDetailVOLsit.get(i).getStartTime());
                sagEvent.setFirstTime(PubUtils.ms2Date(ll));// 必须增加，否则序列化出错
                SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

                /*String tem = advanceEventDetailVOLsit.get(i).getFirstTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                sagEvent.setStrTime(tem + "."
                        + advanceEventDetailVOLsit.get(i).getFirstMs());*/
                sagEvent.setTime(Timestamp.valueOf(advanceEventDetailVOLsit.get(i).getStartTime()).getTime());
                sagEvent.setFirstTimeMills(ll);
                sagEvent.setMsec(advanceEventDetailVOLsit.get(i).getDuration().intValue());
                //sagEvent.setSagTime(PubUtils.ms2Date(Timestamp.valueOf(advanceEventDetailVOLsit.get(i).getFirstTime()).getTime()));
                PlantInfo plantInfo = new PlantInfo();
                plantInfo.setNameBD(advanceEventDetailVOLsit.get(i).getSubName());
                plantInfo.setNameGD(advanceEventDetailVOLsit.get(i).getGdName());
                plantInfo.setNamePoint(advanceEventDetailVOLsit.get(i).getLineId());
                sagEvent.setPlantInfo(plantInfo);
                sagEvent.setIndexPoint(advanceEventDetailVOLsit.get(i).getLineId());
                sagEvent.setCata(cata);


                EntityGroupEvtData entityGroupEvtData = new EntityGroupEvtData(nodePhysics, start_time, cata, -1, sagEvent, advanceEventDetailVOLsit.get(i).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<EventAssObj> 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<SagEvent>();

                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.size() > 0) {
                    processing(listTem, assEvent, date);
                }
            }
        }

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

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

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


            // 获取当前用户GUID
            eventAssObj.setIndexUser(RequestUtil.getUserIndex());

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

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


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

            eventAssObj.setList(list);
            lists.add(eventAssObj);
        }
    }


    /*************************************************************************************
     * 获取变压器信息并生成矩阵
     *************************************************************************************/
    public void getNodeInfo(HandleEvent handleEvent) {
        List<EntityLogic> list = relevantMapper.getLogic();

        if (CollectionUtil.isNotEmpty(list)) {
            Map<String, List<EntityLogic>> tfMap = list.stream().collect(Collectors.groupingBy(EntityLogic::getTPIndex));
            Map<String, List<String>> tfBusBarMap = new HashMap<>();

            Map<String, EntityMtrans> entityMtranMap = new HashMap<>();

            tfMap.forEach((key, val) -> {
                List<String> tem = new ArrayList<>();
                val.forEach(it -> {
                    tem.add(it.getNodeBefore());
                    tem.add(it.getNodeNext());
                });
                tfBusBarMap.put(key, tem);

                EntityMtrans entityMtrans = new EntityMtrans();
                handleEvent.create_matrixcata(val, entityMtrans);
                entityMtranMap.put(key, entityMtrans);
            });


        }
    }



    public List<AdvanceEventDetailVO> querySagEventsAll(LocalDateTime startTime,LocalDateTime endTime) {
        List<String> lineIds = generalDeviceInfoClient.deptGetRunLineEvent(RequestUtil.getDeptIndex()).getData();
        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);


            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<AreaLineInfoVO> temLine = lineFeignClient.getBaseLineAreaInfo(tempLineIds).getData();
            Map<String, AreaLineInfoVO> map = temLine.stream().collect(Collectors.toMap(AreaLineInfoVO::getLineId, Function.identity()));

            List<AdvanceEventDetailVO> advanceEventDetailVOList = BeanUtil.copyToList(rmpEventDetailPOList, AdvanceEventDetailVO.class);
            advanceEventDetailVOList = advanceEventDetailVOList.stream().peek(item -> {
                if (map.containsKey(item.getLineId())) {
                    AreaLineInfoVO areaLineInfoVO = map.get(item.getLineId());
                    item.setGdName(areaLineInfoVO.getGdName());
                    item.setSubName(areaLineInfoVO.getSubName());
                    item.setNum(areaLineInfoVO.getNum());
                    item.setVoltageId(areaLineInfoVO.getVoltageId());
                }
            }).collect(Collectors.toList());

            return advanceEventDetailVOList;
        }
        return new ArrayList<>();
    }
}
