package cn.stylefeng.workflow.instance.core.flow;

import cn.hutool.core.util.ObjectUtil;
import cn.stylefeng.roses.kernel.rule.constants.TreeConstants;
import cn.stylefeng.workflow.api.entity.design.Node;
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.api.enums.InstanceStatusEnum;
import cn.stylefeng.workflow.api.enums.NodeStatusEnum;
import cn.stylefeng.workflow.api.pojo.InstanceContext;
import cn.stylefeng.workflow.instance.api.InstanceVariableApi;
import cn.stylefeng.workflow.instance.api.WorkflowStatusApi;
import cn.stylefeng.workflow.instance.api.factory.RuntimeNodeEdgeFactory;
import cn.stylefeng.workflow.instance.api.factory.RuntimeNodeFactory;
import cn.stylefeng.workflow.instance.api.pojo.NodeActionResult;
import cn.stylefeng.workflow.instance.core.nodeexe.NodeExecutor;
import cn.stylefeng.workflow.instance.modular.manage.service.RuntimeInstanceService;
import cn.stylefeng.workflow.instance.modular.manage.service.RuntimeNodeEdgeService;
import cn.stylefeng.workflow.instance.modular.manage.service.RuntimeNodeService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
public class SyncWorkflowEngine {

    @Resource
    private RuntimeNodeEdgeService runtimeNodeEdgeService;

    @Resource
    private NodeExecutor nodeExecutor;

    @Resource
    private InstanceVariableApi variableApi;

    @Resource
    private RuntimeInstanceService runtimeInstanceService;

    @Resource
    private RuntimeNodeService runtimeNodeService;

    @Resource
    private WorkflowStatusApi workflowStatusApi;

    @Transactional(rollbackFor = Exception.class)
    public void begin(RuntimeNode lastRuntimeNodeList, Node currentNode, InstanceContext instanceContext) {

        if (currentNode == null) {
            return;
        }

        Long instanceId = instanceContext.getRuntimeInstance().getInstanceId();
        RuntimeInstance latestInstance = runtimeInstanceService.getInstanceInfById(instanceId);
        if (latestInstance.getInstanceStatus() >= 30) {

            RuntimeNode runtimeNode = RuntimeNodeFactory.initRuntimeNode(currentNode, instanceId);
            runtimeNode.setNodeStatus(NodeStatusEnum.NOT_RUN.getStatus());
            runtimeNodeService.save(runtimeNode);

            if (lastRuntimeNodeList != null) {
                RuntimeNodeEdge edge = RuntimeNodeEdgeFactory.createRuEdges(lastRuntimeNodeList, runtimeNode,
                        instanceContext.getRuntimeInstance().getInstanceId());
                runtimeNodeEdgeService.save(edge);
            }

            return;
        }

        RuntimeInstance runtimeInstance = instanceContext.getRuntimeInstance();
        if (runtimeInstance.getInstanceStatus().equals(InstanceStatusEnum.FINISH.getStatus())) {
            return;
        }

        NodeActionResult nodeActionResult = nodeExecutor.executeNodeAction(currentNode, instanceContext);


        instanceContext.setRuntimeNode(nodeActionResult.getCurrentRuntimeNode());

        instanceContext.setInstanceVariable(nodeActionResult.getNewInstanceVariable());
        variableApi.updateVariableJson(instanceContext);

        RuntimeNode currentRuntimeNode = nodeActionResult.getCurrentRuntimeNode();
        if (lastRuntimeNodeList != null && currentRuntimeNode != null) {
            RuntimeNodeEdge edge = RuntimeNodeEdgeFactory.createRuEdges(lastRuntimeNodeList, currentRuntimeNode,
                    instanceContext.getRuntimeInstance().getInstanceId());
            runtimeNodeEdgeService.save(edge);
        }

        if (!nodeActionResult.isChangeInstancePath()) {
            this.processNormalPathInstance(instanceContext, nodeActionResult);
        }

        else {
            this.processChangeInstancePath(nodeActionResult);
        }
    }

    private void processNormalPathInstance(InstanceContext instanceContext, NodeActionResult nodeActionResult) {

        List<Node> nextNodeList = nodeActionResult.getNextNodeList();
        if (ObjectUtil.isNotEmpty(nextNodeList)) {
            for (Node nextNodeItem : nextNodeList) {
                this.begin(nodeActionResult.getCurrentRuntimeNode(), nextNodeItem, instanceContext);
            }
        }
    }

    private void processChangeInstancePath(NodeActionResult nodeActionResult) {

        Long parentInstanceId = nodeActionResult.getParentInstanceId();

        if (parentInstanceId == null || TreeConstants.DEFAULT_PARENT_ID.equals(parentInstanceId)) {
            return;
        }

        InstanceContext parentInstanceContext = workflowStatusApi.buildInstanceContext(parentInstanceId);

        List<Node> nextNodeList = nodeActionResult.getNextNodeList();
        if (ObjectUtil.isNotEmpty(nextNodeList)) {
            for (Node nextNodeItem : nextNodeList) {
                this.begin(null, nextNodeItem, parentInstanceContext);
            }
        }
    }

}
