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

import cn.hutool.core.util.ObjectUtil;
import cn.stylefeng.workflow.api.entity.design.Node;
import cn.stylefeng.workflow.api.entity.design.NodeEdge;
import cn.stylefeng.workflow.api.enums.NodeTypeEnum;
import cn.stylefeng.workflow.api.pojo.InstanceContext;
import lombok.AllArgsConstructor;
import lombok.Data;

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

@AllArgsConstructor
@Data
public class NodePathCalc {

    private List<Node> definitionNodes;

    private List<NodeEdge> definitionNodeEdges;

    public NodePathCalc(InstanceContext instanceContext) {
        this.definitionNodes = instanceContext.getDefinitionNodeList();
        this.definitionNodeEdges = instanceContext.getDefinitionNodeEdgeList();
    }

    public Node getStartNode() {
        for (Node definitionNode : definitionNodes) {
            if (NodeTypeEnum.START.getNodeType().equals(definitionNode.getNodeType())) {
                return definitionNode;
            }
        }
        return null;
    }

    public Node getPointNode(NodeTypeEnum nodeTypeEnum) {
        for (Node definitionNode : definitionNodes) {
            if (nodeTypeEnum.getNodeType().equals(definitionNode.getNodeType())) {
                return definitionNode;
            }
        }
        return null;
    }

    public List<Node> calcNextNodeList(Long currentNodeId) {

        Map<Long, Node> nodeMap = definitionNodes.stream().collect(Collectors.toMap(Node::getNodeId, node -> node));

        ArrayList<Node> nextNodeList = new ArrayList<>();

        for (NodeEdge definitionNodeEdge : this.definitionNodeEdges) {
            if (definitionNodeEdge.getSourceNodeId().equals(currentNodeId)) {
                Long targetNodeId = definitionNodeEdge.getTargetNodeId();
                Node targetNode = nodeMap.get(targetNodeId);
                nextNodeList.add(targetNode);
            }
        }

        return nextNodeList;
    }

    public List<Node> calcNextConditionNodeList(Long currentNodeId) {

        List<Node> nodes = this.calcNextNodeList(currentNodeId);

        return nodes.stream().filter(node -> NodeTypeEnum.CONDITION_GATEWAY_CONDITION.getNodeType().equals(node.getNodeType())).collect(Collectors.toList());
    }

    public List<Long> getNodeBeforeNodeIdList(Long nodeId) {

        List<Long> result = new ArrayList<>();

        if (ObjectUtil.isEmpty(nodeId)) {
            return result;
        }

        for (NodeEdge definitionNodeEdge : definitionNodeEdges) {
            if (nodeId.equals(definitionNodeEdge.getTargetNodeId())) {
                result.add(definitionNodeEdge.getSourceNodeId());
            }
        }

        return result;
    }

    public Long getNodeBeforeApproveNode(Long nodeId) {

        if (ObjectUtil.isEmpty(nodeId)) {
            return null;
        }

        for (NodeEdge definitionNodeEdge : definitionNodeEdges) {
            if (definitionNodeEdge.getTargetNodeId().equals(nodeId)) {

                Long sourceNodeId = definitionNodeEdge.getSourceNodeId();
                Node sourceNode = definitionNodes.stream().filter(node -> node.getNodeId().equals(sourceNodeId)).findFirst().orElse(null);

                if (sourceNode == null) {
                    return null;
                }

                if (NodeTypeEnum.APPLICANT.getNodeType().equals(sourceNode.getNodeType())) {
                    return null;
                } else if (NodeTypeEnum.START.getNodeType().equals(sourceNode.getNodeType())) {
                    return null;
                } else if (NodeTypeEnum.USER_APPROVAL.getNodeType().equals(sourceNode.getNodeType())) {
                    return sourceNodeId;
                } else {

                    return getNodeBeforeApproveNode(sourceNodeId);
                }
            }
        }

        return null;
    }

}
