package cn.stylefeng.workflow.instance.modular.manage.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.stylefeng.roses.kernel.db.api.factory.PageFactory;
import cn.stylefeng.roses.kernel.db.api.factory.PageResultFactory;
import cn.stylefeng.roses.kernel.db.api.pojo.page.PageResult;
import cn.stylefeng.roses.kernel.rule.exception.base.ServiceException;
import cn.stylefeng.workflow.api.NodeApi;
import cn.stylefeng.workflow.api.entity.design.NodeEdge;
import cn.stylefeng.workflow.api.entity.instance.RuntimeInstance;
import cn.stylefeng.workflow.api.entity.instance.RuntimeNode;
import cn.stylefeng.workflow.api.entity.instance.RuntimeNodeEdge;
import cn.stylefeng.workflow.instance.api.InstanceApi;
import cn.stylefeng.workflow.instance.modular.manage.enums.RuntimeNodeEdgeExceptionEnum;
import cn.stylefeng.workflow.instance.modular.manage.mapper.RuntimeNodeEdgeMapper;
import cn.stylefeng.workflow.instance.modular.manage.pojo.request.RuntimeNodeEdgeRequest;
import cn.stylefeng.workflow.instance.modular.manage.service.RuntimeNodeEdgeService;
import cn.stylefeng.workflow.instance.modular.manage.service.RuntimeNodeService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

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

@Service
public class RuntimeNodeEdgeServiceImpl extends ServiceImpl<RuntimeNodeEdgeMapper, RuntimeNodeEdge> implements RuntimeNodeEdgeService {

    @Resource
    private InstanceApi instanceApi;

    @Resource
    private NodeApi nodeApi;

    @Resource
    private RuntimeNodeService runtimeNodeService;

    @Override
    public void add(RuntimeNodeEdgeRequest runtimeNodeEdgeRequest) {
        RuntimeNodeEdge runtimeNodeEdge = new RuntimeNodeEdge();
        BeanUtil.copyProperties(runtimeNodeEdgeRequest, runtimeNodeEdge);
        this.save(runtimeNodeEdge);
    }

    @Override
    public void del(RuntimeNodeEdgeRequest runtimeNodeEdgeRequest) {
        RuntimeNodeEdge runtimeNodeEdge = this.queryRuntimeNodeEdge(runtimeNodeEdgeRequest);
        this.removeById(runtimeNodeEdge.getRuNodeEdgeId());
    }

    @Override
    public void edit(RuntimeNodeEdgeRequest runtimeNodeEdgeRequest) {
        RuntimeNodeEdge runtimeNodeEdge = this.queryRuntimeNodeEdge(runtimeNodeEdgeRequest);
        BeanUtil.copyProperties(runtimeNodeEdgeRequest, runtimeNodeEdge);
        this.updateById(runtimeNodeEdge);
    }

    @Override
    public RuntimeNodeEdge detail(RuntimeNodeEdgeRequest runtimeNodeEdgeRequest) {
        return this.queryRuntimeNodeEdge(runtimeNodeEdgeRequest);
    }

    @Override
    public PageResult<RuntimeNodeEdge> findPage(RuntimeNodeEdgeRequest runtimeNodeEdgeRequest) {
        LambdaQueryWrapper<RuntimeNodeEdge> wrapper = createWrapper(runtimeNodeEdgeRequest);
        Page<RuntimeNodeEdge> sysRolePage = this.page(PageFactory.defaultPage(), wrapper);
        return PageResultFactory.createPageResult(sysRolePage);
    }

    @Override
    public List<Long> getHighLightNodeEdgeList(Long instanceId) {

        RuntimeInstance runtimeInstance = instanceApi.getInstanceInfById(instanceId);

        Long definitionId = runtimeInstance.getDefinitionId();

        List<NodeEdge> definitionEdgeList = nodeApi.getDefinitionEdgeList(definitionId);
        if (ObjectUtil.isEmpty(definitionEdgeList)) {
            return new ArrayList<>();
        }

        RuntimeNodeEdgeRequest runtimeNodeEdgeRequest = new RuntimeNodeEdgeRequest();
        runtimeNodeEdgeRequest.setInstanceId(instanceId);
        List<RuntimeNodeEdge> runtimeNodeEdgeList = this.findList(runtimeNodeEdgeRequest);
        if (ObjectUtil.isEmpty(runtimeNodeEdgeList)) {
            return new ArrayList<>();
        }

        List<Long> ruNodeIdListOne = runtimeNodeEdgeList.stream().map(RuntimeNodeEdge::getSourceNodeId).collect(Collectors.toList());
        List<Long> ruNodeIdListTwo = runtimeNodeEdgeList.stream().map(RuntimeNodeEdge::getTargetNodeId).collect(Collectors.toList());
        ruNodeIdListOne.addAll(ruNodeIdListTwo);

        List<RuntimeNode> runtimeNodes = this.runtimeNodeService.listByIds(ruNodeIdListOne);

        Map<Long, Long> keyRuNodeIdValueNodeId = runtimeNodes.stream().collect(Collectors.toMap(RuntimeNode::getRuNodeId, RuntimeNode::getNodeId));

        ArrayList<Long> highLightNodeEdges = new ArrayList<>();
        for (RuntimeNodeEdge runtimeNodeEdge : runtimeNodeEdgeList) {

            Long sourceNodeId = keyRuNodeIdValueNodeId.get(runtimeNodeEdge.getSourceNodeId());
            Long targetNodeId = keyRuNodeIdValueNodeId.get(runtimeNodeEdge.getTargetNodeId());

            for (NodeEdge nodeEdge : definitionEdgeList) {
                if (nodeEdge.getSourceNodeId().equals(sourceNodeId) && nodeEdge.getTargetNodeId().equals(targetNodeId)) {
                    highLightNodeEdges.add(nodeEdge.getEdgeId());
                }
            }
        }

        return highLightNodeEdges;
    }

    @Override
    public List<RuntimeNodeEdge> findList(RuntimeNodeEdgeRequest runtimeNodeEdgeRequest) {
        LambdaQueryWrapper<RuntimeNodeEdge> wrapper = this.createWrapper(runtimeNodeEdgeRequest);
        return this.list(wrapper);
    }

    private RuntimeNodeEdge queryRuntimeNodeEdge(RuntimeNodeEdgeRequest runtimeNodeEdgeRequest) {
        RuntimeNodeEdge runtimeNodeEdge = this.getById(runtimeNodeEdgeRequest.getRuNodeEdgeId());
        if (ObjectUtil.isEmpty(runtimeNodeEdge)) {
            throw new ServiceException(RuntimeNodeEdgeExceptionEnum.RUNTIME_NODE_EDGE_NOT_EXISTED);
        }
        return runtimeNodeEdge;
    }

    private LambdaQueryWrapper<RuntimeNodeEdge> createWrapper(RuntimeNodeEdgeRequest runtimeNodeEdgeRequest) {
        LambdaQueryWrapper<RuntimeNodeEdge> queryWrapper = new LambdaQueryWrapper<>();

        Long instanceId = runtimeNodeEdgeRequest.getInstanceId();
        queryWrapper.eq(ObjectUtil.isNotNull(instanceId), RuntimeNodeEdge::getInstanceId, instanceId);

        return queryWrapper;
    }

}