package com.njcn.csdevice.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.csdevice.mapper.CsTerminalLogsMapper;
import com.njcn.csdevice.mapper.CsTerminalReplyMapper;
import com.njcn.csdevice.pojo.po.*;
import com.njcn.csdevice.service.CsEquipmentDeliveryService;
import com.njcn.csdevice.service.CsLinePOService;
import com.njcn.csdevice.service.ICsTerminalLogsService;
import com.njcn.csdevice.service.INodeService;
import com.njcn.mq.message.CldControlMessage;
import com.njcn.mq.message.CldUpdateLedgerMessage;
import com.njcn.mq.template.CldControlMessageTemplate;
import com.njcn.mq.template.CldUpdateLedgerMessageTemplate;
import com.njcn.redis.utils.RedisUtil;
import com.njcn.system.api.DictTreeFeignClient;
import com.njcn.web.utils.RequestUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author xy
 * @since 2025-09-26
 */
@Service
@RequiredArgsConstructor
public class CsTerminalLogsServiceImpl extends ServiceImpl<CsTerminalLogsMapper, CsTerminalLogs> implements ICsTerminalLogsService {

    private final CsEquipmentDeliveryService csEquipmentDeliveryService;
    private final CsLinePOService csLinePOService;
    private final DictTreeFeignClient dictTreeFeignClient;
    private final INodeService nodeService;
    private final CldUpdateLedgerMessageTemplate template;
    private final CldControlMessageTemplate  controlMessageTemplate;
    private final RedisUtil redisUtil;
    private final CsTerminalReplyMapper csTerminalReplyMapper;

    @Override
    public void updateLogs(String id, Integer code) {
        LambdaUpdateWrapper<CsTerminalLogs> wrapper = new LambdaUpdateWrapper<>();
        wrapper.set(CsTerminalLogs::getIsPush, 0)
                .eq(CsTerminalLogs::getDeviceId, id);
        this.update(wrapper);
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public String pushCldInfo() {
        LambdaQueryWrapper<CsTerminalLogs> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CsTerminalLogs::getIsPush, 0);
        List<CsTerminalLogs> list = this.list(wrapper);
        if (CollectionUtil.isNotEmpty(list)) {
            //先清空缓存
            redisUtil.delete(RequestUtil.getUserIndex()+"reply");
            //新增台账集合
            List<String> addList = new ArrayList<>();
            //修改台账集合
            List<String> updateList = new ArrayList<>();
            //删除台账集合
            List<String> deleteList = new ArrayList<>();

            //根据设备分组，判断设备的操作，整合
            Map<String,List<CsTerminalLogs>> map = list.stream().collect(Collectors.groupingBy(CsTerminalLogs::getDeviceId));
            map.forEach((k,v)->{
                List<Integer> operateTypeList = v.stream().map(CsTerminalLogs::getOperateType).distinct().collect(Collectors.toList());

                if (operateTypeList.size() == 3) {
                    //啥也不用干
                    //deleteList.add(k);
                } else if (operateTypeList.size() == 2 && operateTypeList.contains(0) && operateTypeList.contains(1)) {
                    addList.add(k);
                } else if (operateTypeList.size() == 2 && operateTypeList.contains(0) && operateTypeList.contains(2)) {
                    //啥也不用干
                } else if (operateTypeList.size() == 2 && operateTypeList.contains(1) && operateTypeList.contains(2)) {
                    deleteList.add(k);
                } else if (operateTypeList.size() == 1 && operateTypeList.contains(0)) {
                    addList.add(k);
                } else if (operateTypeList.size() == 1 && operateTypeList.contains(1)) {
                    updateList.add(k);
                } else if (operateTypeList.size() == 1 && operateTypeList.contains(2)) {
                    deleteList.add(k);
                }
            });
            //整合后 所有设备的id
            List<String> devList = Stream.of(addList, updateList, deleteList).flatMap(List::stream).collect(Collectors.toList());
            if (CollectionUtil.isNotEmpty(devList)) {
                //获取设备集合
                List<CsEquipmentDeliveryPO> deviceList = csEquipmentDeliveryService.listByIds(devList);
                //按照前置机id分组
                Map<String,List<CsEquipmentDeliveryPO>> nodeMap = deviceList.stream().collect(Collectors.groupingBy(CsEquipmentDeliveryPO::getNodeId));
                nodeMap.forEach((k,v)->{
                    int maxProcessNum = nodeService.getNodeById(k).getMaxProcessNum();
                    //按照进程号分组
                    Map<Integer,List<CsEquipmentDeliveryPO>> nodeProcessMap = v.stream().collect(Collectors.groupingBy(CsEquipmentDeliveryPO::getNodeProcess));
                    nodeProcessMap.forEach((k1,v1)->{
                        if (v1.size() > 10) {
                            //一个进程下修改的设备数量超过10台，重启该进程号下的前置
                            CldControlMessage cldControlMessage = new CldControlMessage();
                            cldControlMessage.setGuid(IdUtil.simpleUUID());
                            cldControlMessage.setCode("set_process");
                            cldControlMessage.setProcessNo(k1);
                            cldControlMessage.setFun("delete");
                            cldControlMessage.setProcessNum(maxProcessNum);
                            controlMessageTemplate.sendMember(cldControlMessage,k);
                        }
                    });
                });

                if (CollectionUtil.isNotEmpty(addList)) {
                    sendMessage(addList, deviceList, "add_terminal");
                }
                if (CollectionUtil.isNotEmpty(updateList)) {
                    sendMessage(updateList, deviceList, "ledger_modify");
                }
                if (CollectionUtil.isNotEmpty(deleteList)) {
                    sendDeleteMessage(deleteList, list, "delete_terminal");
                }

                //推送完将数据改成推送
                LambdaUpdateWrapper<CsTerminalLogs> wrapper2 = new LambdaUpdateWrapper<>();
                wrapper2.set(CsTerminalLogs::getIsPush, 1);
                this.update(wrapper2);
            }
        } else {
            return "暂无需要推送的数据";
        }
        return "成功";
    }

    @Override
    public void updateLaterData(String id, String code) {
        int operateType;
        if (Objects.equals(code, "add_terminal")) {
            operateType = 0;
        } else if (Objects.equals(code, "ledger_modify")) {
            operateType = 1;
        } else if (Objects.equals(code, "delete_terminal")){
            operateType = 2;
        } else {
            operateType = 3;
        }
        //找出最新的数据，将状态改为未推送，下次可以再次推送
        this.lambdaUpdate()
                .eq(CsTerminalLogs::getDeviceId, id)
                .eq(CsTerminalLogs::getOperateType, operateType)
                .orderByDesc(CsTerminalLogs::getCreateTime)
                .last("LIMIT 1")
                .set(CsTerminalLogs::getIsPush, 0)
                .update();
    }

    public void sendMessage(List<String> addList, List<CsEquipmentDeliveryPO> deviceList, String type) {
        List<CsEquipmentDeliveryPO> filteredList = deviceList.stream().filter(device -> addList.contains(device.getId())).collect(Collectors.toList());
        //获取监测点信息
        List<CsLinePO> lineList = csLinePOService.getLinesByDevList(addList);

        Map<String,List<CsEquipmentDeliveryPO>> nodeMap1 = filteredList.stream().collect(Collectors.groupingBy(CsEquipmentDeliveryPO::getNodeId));
        nodeMap1.forEach((k,v)->{
            CldUpdateLedgerMessage message = new CldUpdateLedgerMessage();
            message.setCode(type);
            //按照进程号分组
            Map<Integer,List<CsEquipmentDeliveryPO>> nodeProcessMap1 = v.stream().collect(Collectors.groupingBy(CsEquipmentDeliveryPO::getNodeProcess));
            for (Map.Entry<Integer, List<CsEquipmentDeliveryPO>> entry : nodeProcessMap1.entrySet()) {
                Integer k1 = entry.getKey();
                List<CsEquipmentDeliveryPO> v1 = entry.getValue();
                String guid = IdUtil.simpleUUID();
                message.setGuid(guid);
                message.setProcessNo(k1);
                List<CldUpdateLedgerMessage.CldDeviceDto> list1 = new ArrayList<>();
                v1.forEach(item -> {
                    CldUpdateLedgerMessage.CldDeviceDto deviceDto = new CldUpdateLedgerMessage.CldDeviceDto();
                    deviceDto.setId(item.getId());
                    deviceDto.setNode(item.getNodeProcess());
                    deviceDto.setName(item.getName());
                    deviceDto.setIp(item.getMac());
                    deviceDto.setDevType(dictTreeFeignClient.queryById(item.getDevModel()).getData().getName());
                    Node node = nodeService.getNodeById(item.getNodeId());
                    if (node != null) {
                        deviceDto.setMaxProcessNum(node.getMaxProcessNum());
                    }

                    if (CollectionUtil.isNotEmpty(lineList)) {
                        List<CldUpdateLedgerMessage.CldMonitorDto> list2 = new ArrayList<>();
                        lineList.forEach(item2 -> {
                            if (Objects.equals(item.getId(), item2.getDeviceId())) {
                                CldUpdateLedgerMessage.CldMonitorDto cldLineDto = new CldUpdateLedgerMessage.CldMonitorDto();
                                cldLineDto.setId(item2.getLineId());
                                cldLineDto.setName(item.getName());
                                cldLineDto.setLineNo(String.valueOf(item2.getLineNo()));
                                cldLineDto.setVoltageLevel(item2.getVolGrade() + "kV");
                                cldLineDto.setStatus(item.getRunStatus());
                                cldLineDto.setPtType(String.valueOf(item2.getConType()));
                                cldLineDto.setDeviceId(item2.getDeviceId());
                                cldLineDto.setPt1(item2.getPtRatio());
                                cldLineDto.setPt2(item2.getPt2Ratio());
                                cldLineDto.setCt1(item2.getCtRatio());
                                cldLineDto.setCt2(item2.getCt2Ratio());
                                list2.add(cldLineDto);
                            }
                        });
                        deviceDto.setMonitorData(list2);
                    }
                    list1.add(deviceDto);

                    //存储发送的数据记录，用于后面响应校验
                    CsTerminalReply csFmTerminalReply = new CsTerminalReply();
                    csFmTerminalReply.setReplyId(guid);
                    csFmTerminalReply.setCode(type);
                    csFmTerminalReply.setNodeId(k);
                    csFmTerminalReply.setProcessNo(k1);
                    csFmTerminalReply.setDeviceId(item.getId());
                    csFmTerminalReply.setIsReceived(0);
                    csTerminalReplyMapper.insert(csFmTerminalReply);
                });
                message.setData(list1);
                template.sendMember(message, k);

                //缓存本次发送数据GUID的集合，用于查询数据
                Object object = redisUtil.getObjectByKey(RequestUtil.getUserIndex()+"reply");
                if (object != null) {
                    String value = object + "," + guid;
                    redisUtil.saveByKeyWithExpire(RequestUtil.getUserIndex()+"reply", value, 60L);
                } else {
                    redisUtil.saveByKeyWithExpire(RequestUtil.getUserIndex()+"reply", guid, 60L);
                }
            }
        });
    }

    public void sendDeleteMessage(List<String> list, List<CsTerminalLogs> csTerminalLogs, String type) {
        List<CsTerminalLogs> filteredList = csTerminalLogs.stream().filter(device -> list.contains(device.getDeviceId()) && Objects.equals(device.getOperateType(),2)).collect(Collectors.toList());
        Map<String,List<CsTerminalLogs>> nodeMap1 = filteredList.stream().collect(Collectors.groupingBy(CsTerminalLogs::getNodeId));
        nodeMap1.forEach((k,v)->{
            CldUpdateLedgerMessage message = new CldUpdateLedgerMessage();
            message.setCode(type);
            //按照进程号分组
            Map<Integer,List<CsTerminalLogs>> nodeProcessMap1 = v.stream().collect(Collectors.groupingBy(CsTerminalLogs::getNodeProcess));
            nodeProcessMap1.forEach((k1,v1)->{
                String guid = IdUtil.simpleUUID();
                message.setGuid(guid);
                message.setProcessNo(k1);
                List<CldUpdateLedgerMessage.CldDeviceDto> list1 = new ArrayList<>();
                v1.forEach(item->{
                    CldUpdateLedgerMessage.CldDeviceDto deviceDto = new CldUpdateLedgerMessage.CldDeviceDto();
                    deviceDto.setId(item.getDeviceId());
                    deviceDto.setNode(item.getNodeProcess());
                    Node node = nodeService.getNodeById(item.getNodeId());
                    if (node != null) {
                        deviceDto.setMaxProcessNum(node.getMaxProcessNum());
                    }
                    list1.add(deviceDto);

                    //存储发送的数据记录，用于后面响应校验
                    CsTerminalReply csFmTerminalReply = new CsTerminalReply();
                    csFmTerminalReply.setReplyId(guid);
                    csFmTerminalReply.setCode(type);
                    csFmTerminalReply.setNodeId(k);
                    csFmTerminalReply.setProcessNo(k1);
                    csFmTerminalReply.setDeviceId(item.getDeviceId());
                    csFmTerminalReply.setDeviceName(item.getDeviceName());
                    csFmTerminalReply.setIsReceived(0);
                    csTerminalReplyMapper.insert(csFmTerminalReply);
                });
                message.setData(list1);
                template.sendMember(message,k);

                //缓存本次发送数据GUID的集合，用于查询数据
                Object object = redisUtil.getObjectByKey(RequestUtil.getUserIndex()+"reply");
                if (object != null) {
                    String value = object + "," + guid;
                    redisUtil.saveByKeyWithExpire(RequestUtil.getUserIndex()+"reply", value, 60L);
                } else {
                    redisUtil.saveByKeyWithExpire(RequestUtil.getUserIndex()+"reply", guid, 60L);
                }
            });
        });
    }
}
