package com.njcn.csdevice.service.impl;


import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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.csdevice.enums.AlgorithmResponseEnum;
import com.njcn.csdevice.mapper.CsEquipmentDeliveryMapper;
import com.njcn.csdevice.mapper.NodeMapper;
import com.njcn.csdevice.param.IcdNodeParam;
import com.njcn.csdevice.pojo.param.NodeParam;
import com.njcn.csdevice.pojo.po.CsEquipmentDeliveryPO;
import com.njcn.csdevice.pojo.po.Node;
import com.njcn.csdevice.pojo.vo.NodeProcessDeviceVo;
import com.njcn.csdevice.service.INodeService;
import com.njcn.db.constant.DbConstant;
import com.njcn.device.biz.enums.DeviceResponseEnum;
import com.njcn.mq.message.CldControlMessage;
import com.njcn.mq.template.CldControlMessageTemplate;
import com.njcn.web.factory.PageFactory;
import lombok.AllArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author cdf
 * @since 2022-01-07
 */
@DS("sjzx")
@Service
@AllArgsConstructor
public class NodeServiceImpl extends ServiceImpl<NodeMapper, Node> implements INodeService {

    private final CsEquipmentDeliveryMapper csEquipmentDeliveryMapper;
    private final CldControlMessageTemplate controlMessageTemplate;

    @Override
    public boolean addNode(NodeParam nodeParam) {
        checkNode(nodeParam, false);
        Node node = new Node();
        BeanUtils.copyProperties(nodeParam, node);
        node.setState(DataStateEnum.ENABLE.getCode());
        this.save(node);
        return true;
    }

    @Override
    public boolean updateNode(NodeParam.NodeUpdateParam nodeParam) {
        //修改最大进程时，需要判断，如果进程数减少，需要先将少的进程数下面的测点分配到其他进程下面
        int count = csEquipmentDeliveryMapper.getListByNodeProcess(nodeParam.getId(),nodeParam.getMaxProcessNum());
        if (count > 0) {
            throw new BusinessException(AlgorithmResponseEnum.DEV_OLD_DATA);
        }
        checkNode(nodeParam, true);
        Node node = new Node();
        BeanUtils.copyProperties(nodeParam, node);
        this.updateById(node);
        return true;
    }

    @Override
    public boolean delNode(String id) {
        //删除前置机，如果前置机下有设备，不允许删除
        int count = csEquipmentDeliveryMapper.getListByNodeProcess(id,null);
        if (count > 0) {
            throw new BusinessException(AlgorithmResponseEnum.DEV_EXIST_DATA);
        }
        return this.removeById(id);
    }

    @Override
    public boolean updateNodeState(String id, Integer state) {
        Node node = new Node();
        node.setId(id);
        node.setState(state);
        return this.updateById(node);
    }


    @Override
    public Page<Node> nodeList(NodeParam.NodeQueryParam nodeQueryParam) {
        QueryWrapper<Node> queryWrapper = new QueryWrapper<>();
        //查询参数不为空，进行条件填充
        if (Objects.nonNull(nodeQueryParam)) {
            if(Objects.nonNull(nodeQueryParam.getNodeGrade()) ){
                queryWrapper.eq("pq_node.node_grade",nodeQueryParam.getNodeGrade());
            }
            if(Objects.nonNull(nodeQueryParam.getSearchState()) ){
                queryWrapper.eq("pq_node.state",nodeQueryParam.getSearchState());
            }
            //模糊值查询
            if (StrUtil.isNotBlank(nodeQueryParam.getSearchValue())) {
                //仅提供名称、ip模糊查询
                queryWrapper.and(param -> param.like("pq_node.name", nodeQueryParam.getSearchValue())
                        .or().like("pq_node.ip", nodeQueryParam.getSearchValue()));
            }
            //排序字段不为空
            if (ObjectUtil.isAllNotEmpty(nodeQueryParam.getSortBy(), nodeQueryParam.getOrderBy())) {
                queryWrapper.orderBy(true, nodeQueryParam.getOrderBy().equals(DbConstant.ASC), StrUtil.toUnderlineCase(nodeQueryParam.getSortBy()));
            } else {
                //没有排序参数，默认根据sort字段排序，没有排序字段的，根据updateTime更新时间排序
                queryWrapper.orderBy(true, true, "pq_node.sort");
            }
        }
        return this.page(new Page<>(PageFactory.getPageNum(nodeQueryParam), PageFactory.getPageSize(nodeQueryParam)), queryWrapper);
    }

    @Override
    public  List<Node> nodeAllList(){
        LambdaQueryWrapper<Node> nodeLambdaQueryWrapper = new LambdaQueryWrapper<>();
        nodeLambdaQueryWrapper.eq(Node::getState,DataStateEnum.ENABLE.getCode()).orderByAsc(Node::getState);
        return this.list(nodeLambdaQueryWrapper);
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public Node getNodeById(String id) {
        return this.getById(id);
    }

    @Override
    public Node getNodeByNodeName(String nodeName) {
        LambdaQueryWrapper<Node> nodeLambdaQueryWrapper = new LambdaQueryWrapper<>();
        nodeLambdaQueryWrapper.eq(Node::getName, nodeName)
                .eq(Node::getState, DataStateEnum.ENABLE.getCode());
        return this.baseMapper.selectOne(nodeLambdaQueryWrapper);
    }

    @Override
    public Node getNodeByIp(String ip) {
        LambdaQueryWrapper<Node> nodeLambdaQueryWrapper = new LambdaQueryWrapper<>();
        nodeLambdaQueryWrapper.eq(Node::getIp, ip)
                .eq(Node::getState, DataStateEnum.ENABLE.getCode());
        return this.baseMapper.selectOne(nodeLambdaQueryWrapper);
    }

    @Override
    public NodeProcessDeviceVo getProcessNoAndDeviceById(String id) {
        NodeProcessDeviceVo nodeProcessDeviceVo = new NodeProcessDeviceVo();
        Node node = this.getById(id);
        nodeProcessDeviceVo.setNode(node);
        List<NodeProcessDeviceVo.ProcessDevice> processDeviceList = new ArrayList<>();
        List<CsEquipmentDeliveryPO> devList = csEquipmentDeliveryMapper.getDataByNodeIdAndProcessNo(id,null);
        for (int i = 1; i <= node.getMaxProcessNum(); i++) {
            int finalI = i;
            NodeProcessDeviceVo.ProcessDevice processDevice = new NodeProcessDeviceVo.ProcessDevice();
            processDevice.setProcessNo(i);
            processDevice.setDeviceInfoList(devList.stream().filter(dev -> Objects.equals(dev.getNodeProcess(), finalI)).collect(Collectors.toList()));
            processDeviceList.add(processDevice);
        }
        nodeProcessDeviceVo.setProcessDeviceList(processDeviceList);
        return nodeProcessDeviceVo;
    }

    @Override
    public void restartProcess(String nodeId, Integer processNo) {
        Node node = getNodeById(nodeId);
        CldControlMessage cldControlMessage = new CldControlMessage();
        cldControlMessage.setGuid(IdUtil.simpleUUID());
        cldControlMessage.setCode("set_process");
        cldControlMessage.setNodeId(nodeId);
        cldControlMessage.setProcessNo(processNo);
        cldControlMessage.setFun("delete");
        cldControlMessage.setProcessNum(node.getMaxProcessNum());
        controlMessageTemplate.sendMember(cldControlMessage,nodeId);
    }

    @Override
    public String updateDevProcessNo(String id, Integer processNo) {
        String result;
        LambdaQueryWrapper<CsEquipmentDeliveryPO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CsEquipmentDeliveryPO::getId,id);
        CsEquipmentDeliveryPO po = csEquipmentDeliveryMapper.selectOne(wrapper);
        if (Objects.equals(po.getNodeProcess(),processNo)) {
            return "修改进程号前后一致，暂不调整";
        }
        int oldProcessNo = po.getNodeProcess();
        po.setNodeProcess(processNo);
        int count = csEquipmentDeliveryMapper.updateById(po);
        if (count > 0) {
            result = "修改成功";
            //重启前置机进程(两个)
            //原来的
            restartProcess(po.getNodeId(), oldProcessNo);
            //修改后的
            restartProcess(po.getNodeId(), processNo);
        } else {
            result = "修改失败";
        }
        return result;
    }

    @Override
    public String updateDevNode(IcdNodeParam param) {
        String result;
        LambdaQueryWrapper<CsEquipmentDeliveryPO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CsEquipmentDeliveryPO::getId,param.getId());
        CsEquipmentDeliveryPO po = csEquipmentDeliveryMapper.selectOne(wrapper);
        if (Objects.equals(po.getNodeId(),param.getNodeId()) && Objects.equals(po.getNodeProcess(),param.getProcessNo())) {
            return "前置机修改前后一致，暂不调整";
        }
        Node node = getNodeById(param.getNodeId());
        List<CsEquipmentDeliveryPO> devList = getListByNodeId(param.getNodeId());
        if (ObjectUtil.isNotEmpty(devList) && devList.size() >= node.getNodeDevNum()) {
            throw new BusinessException (AlgorithmResponseEnum.OVER_MAX_DEV_COUNT);
        }

        int oldProcessNo = po.getNodeProcess();
        po.setNodeId(param.getNodeId());
        po.setNodeProcess(param.getProcessNo());
        int count = csEquipmentDeliveryMapper.updateById(po);
        if (count > 0) {
            result = "修改成功";
            //重启前置机进程(两个)
            //原来的
            restartProcess(po.getNodeId(), oldProcessNo);
            //修改后的
            restartProcess(param.getNodeId(), param.getProcessNo());
        } else {
            result = "修改失败";
        }
        return result;
    }

    //根据前置机id获取装置数量
    public List<CsEquipmentDeliveryPO> getListByNodeId(String nodeId) {
        LambdaQueryWrapper<CsEquipmentDeliveryPO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(CsEquipmentDeliveryPO::getNodeId,nodeId)
                .in(CsEquipmentDeliveryPO::getRunStatus, Arrays.asList(1,2));
        return csEquipmentDeliveryMapper.selectList(lambdaQueryWrapper);
    }

    /**
     * 校验参数，检查是否存在相同编码的字典类型
     */
    private void checkNode(NodeParam nodeParam, boolean isExcludeSelf) {
        LambdaQueryWrapper<Node> nodeLambdaQueryWrapper = new LambdaQueryWrapper<>();
        nodeLambdaQueryWrapper
                .eq(Node::getIp, nodeParam.getIp())
                .eq(Node::getState, DataStateEnum.ENABLE.getCode());
        //更新的时候，需排除当前记录
        if (isExcludeSelf) {
            if (nodeParam instanceof NodeParam.NodeUpdateParam) {
                nodeLambdaQueryWrapper.ne(Node::getId, ((NodeParam.NodeUpdateParam) nodeParam).getId());
            }
        }
        int countByAccount = this.count(nodeLambdaQueryWrapper);
        //大于等于1个则表示重复
        if (countByAccount >= 1) {
            throw new BusinessException(DeviceResponseEnum.NODE_IP_SAME);
        }
    }
}
