package com.njcn.device.device.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.njcn.common.pojo.enums.common.DataStateEnum;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.device.device.service.DeviceProcessService;
import com.njcn.device.device.service.IDeviceService;
import com.njcn.device.device.service.NodeDeviceService;
import com.njcn.device.line.mapper.LineMapper;
import com.njcn.device.node.mapper.NodeMapper;
import com.njcn.device.pq.constant.Param;
import com.njcn.device.pq.enums.PvDeviceResponseEnum;
import com.njcn.device.pq.pojo.dto.DeviceInfo;
import com.njcn.device.pq.pojo.dto.MonitorInfo;
import com.njcn.device.pq.pojo.dto.NodeProcessDeviceTree;
import com.njcn.device.pq.pojo.param.NodeDeviceParam;
import com.njcn.device.pq.pojo.po.*;
import com.njcn.device.terminal.mapper.PqsTerminalLogsMapper;
import com.njcn.redis.utils.RedisUtil;
import com.njcn.system.api.DicDataFeignClient;
import com.njcn.system.enums.DicDataEnum;
import com.njcn.system.enums.DicDataTypeEnum;
import com.njcn.system.pojo.po.DictData;
import com.njcn.web.utils.RequestUtil;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.njcn.device.pq.enums.LineBaseEnum.SUB_LEVEL;

/**
 * Description:
 * Date: 2024/11/5  8:52【需求编号】
 *
 * @author clam
 * @version V1.0.0
 */
@Service
@RequiredArgsConstructor
public class NodeDeviceServiceImpl implements NodeDeviceService {
    private  final NodeMapper nodeMapper;
    private  final LineMapper lineMapper;
    private final IDeviceService iDeviceService;
    private final DeviceProcessService deviceProcessService;
    private final DicDataFeignClient dicDataFeignClient;
    private final PqsTerminalLogsMapper pqsTerminalLogsMapper;

    private  final RedisUtil redisUtil;
    @Override
    public List<DeviceInfo> nodeDeviceList(NodeDeviceParam nodeDeviceParam) {

        List<DeviceInfo> list = nodeMapper.nodeDeviceList(nodeDeviceParam);
        list.forEach(temp->{
            List<MonitorInfo>  monitorInfoList= lineMapper.getLineByDeviceId(temp.getId());
            temp.setMonitorData(monitorInfoList);
        });

        return list;
    }

    @Override
    public void oneKeyDistribution(String nodeId) {
        Node node = nodeMapper.selectById(nodeId);
        Integer nodeDevNum = node.getNodeDevNum();
        Integer maxProcessNum = node.getMaxProcessNum();
        List<Device> list = iDeviceService.lambdaQuery().eq(Device::getNodeId, nodeId).list();
        if(CollectionUtils.isEmpty(list)){
           throw new BusinessException(PvDeviceResponseEnum.NO_DEVICE);
       }
        if(nodeDevNum<list.size()){
            throw new BusinessException(PvDeviceResponseEnum.OVER_LIMIT);
        }
        List<String> deviceIdList = list.stream().map(Device::getId).collect(Collectors.toList());
        List<DeviceProcess> existingProcesses = deviceProcessService.lambdaQuery().in(DeviceProcess::getId, deviceIdList).list();
        if(existingProcesses.size() >= nodeDevNum){
            throw new BusinessException(PvDeviceResponseEnum.OVER_LIMIT);
        }
        List<String> hasProcess = existingProcesses.stream().map(DeviceProcess::getId).distinct().collect(Collectors.toList());

        //过滤掉已经分配的装置
        List<String> needDevice = deviceIdList.stream().filter(it->!hasProcess.contains(it)).collect(Collectors.toList());
        // 无待分配设备直接抛出异常（保留你的原判断）
        if (CollUtil.isEmpty(needDevice)) {
            throw new BusinessException(CommonResponseEnum.FAIL, "不存在未分配的进程的装置");
        }
        //单个进程支持最大装置数
        int maxDevNumPerProcess = (int) Math.ceil((double) nodeDevNum / maxProcessNum);
        Map<Integer, Long> mapCount = existingProcesses.stream().collect(Collectors.groupingBy(DeviceProcess::getProcessNo,Collectors.counting()));
        List<DeviceProcess> poList = new ArrayList<>();
        Iterator<String> deviceIterator = needDevice.iterator();

        for (int processNo = 1; processNo <= maxProcessNum && deviceIterator.hasNext(); processNo++) {
            Long usedCount = mapCount.getOrDefault(processNo, 0L);
            int remainingCapacity = (int) (maxDevNumPerProcess - usedCount);

            if (remainingCapacity <= 0) {
                continue;
            }
            // 分配设备给当前进程
            for (int i = 0; i < remainingCapacity && deviceIterator.hasNext(); i++) {
                String deviceId = deviceIterator.next();
                DeviceProcess deviceProcess = buildDeviceProcess(deviceId, processNo);
                poList.add(deviceProcess);
            }
        }
        if(CollUtil.isEmpty(poList)){
            throw new BusinessException(CommonResponseEnum.FAIL,"不存在可以分配的进程或装置");
        }

        //更新进程号
        deviceProcessService.saveBatch(poList,100);
    }


    private DeviceProcess buildDeviceProcess(String deviceId, Integer processNo) {
        DeviceProcess deviceProcess = new DeviceProcess();
        deviceProcess.setId(deviceId); // 核心修复：设置设备关联字段，而非主键id
        deviceProcess.setProcessNo(processNo);
        return deviceProcess;
    }

    @Override
    public NodeProcessDeviceTree nodeDeviceTree(String nodeId) {
        Node node = nodeMapper.selectById(nodeId);

        NodeProcessDeviceTree nodeProcessDeviceTree = new NodeProcessDeviceTree();
        nodeProcessDeviceTree.setNode(node);
        NodeDeviceParam nodeDeviceParam = new NodeDeviceParam();
        nodeDeviceParam.setIp(node.getIp());
        List<DeviceInfo> list = nodeMapper.nodeDeviceList(nodeDeviceParam);
        List<NodeProcessDeviceTree.ProcessDevice> processDeviceLis = new ArrayList<>();
        for (int i = 1; i <= node.getMaxProcessNum(); i++) {
            NodeProcessDeviceTree.ProcessDevice  processDevice= new NodeProcessDeviceTree.ProcessDevice();
            processDevice.setProcessNo(i);
            int finalI = i;
            List<DeviceInfo> collect = list.stream().filter(deviceInfo -> Objects.equals(deviceInfo.getProcessNo(), finalI)).collect(Collectors.toList());
            processDevice.setDeviceInfoList(collect);
            processDeviceLis.add(processDevice);
            //h获取redis里心跳

            String heartByKey = redisUtil.getStringByKey(nodeId.concat(":").concat(i + ""));
            if(StringUtils.isNoneBlank(heartByKey)){
                processDevice.setProcessState(heartByKey);
            }else {
                processDevice.setProcessState("0");
            }
        }
        nodeProcessDeviceTree.setProcessDeviceList(processDeviceLis);



        return nodeProcessDeviceTree;
    }

    @Override
    public Boolean updateDeviceProcess(DeviceProcess deviceProcess) {
        DeviceProcess byId = deviceProcessService.getById(deviceProcess.getId());
        deviceProcessService.updateById(deviceProcess);
        //记录日志
        DictData dataDic = dicDataFeignClient.getDicDataByCodeAndType(DicDataEnum.PROCESS_PARMETER.getCode(), DicDataTypeEnum.DEV_OPS.getCode()).getData();
        String name = RequestUtil.getUserNickname();
        PqsTerminalLogs pqsTerminalLogs = new PqsTerminalLogs();
        pqsTerminalLogs.setLogsType(dataDic.getId());
        //终端为4
        pqsTerminalLogs.setTerminalType(4);

        List<Line> subList = lineMapper.selectList(null);
        Map<String,Line> subMap = subList.stream().collect(Collectors.toMap(Line::getId, Function.identity()));
        Line dev = subMap.get(deviceProcess.getId());
        String subName = subMap.get(dev.getPids().split(StrUtil.COMMA)[SUB_LEVEL.getCode()]).getName();
        String temLos = "%s进行更新终端进程操作;终端名称 ：%s,由进程%s修改成进程%s";
        String tem = String.format(temLos,name,subName+"_"+dev.getName(),byId.getProcessNo(),deviceProcess.getProcessNo());
        pqsTerminalLogs.setTerminalDescribe(tem);
        pqsTerminalLogs.setState(DataStateEnum.ENABLE.getCode());
        pqsTerminalLogs.setIsPush(0);
        pqsTerminalLogs.setOperateType(Param.UPDATE);
        pqsTerminalLogs.setObjIndex(deviceProcess.getId());
        pqsTerminalLogsMapper.insert(pqsTerminalLogs);
        return true;
    }
}
