package com.junmp.junmpProcess.utils;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.junmp.junmpProcess.dto.json.flowJson.Inout;
import com.junmp.junmpProcess.entity.ProcessTemplates;
import com.junmp.junmpProcess.service.Repository.ProcessTemplateService;
import org.flowable.bpmn.BpmnAutoLayout;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.TaskService;
import org.flowable.engine.delegate.ExecutionListener;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.Resource;
import java.util.*;

import static org.flowable.bpmn.model.ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION;

public class BpmnConvert {

    @Resource
    private IdWorker idWorker;

    private static String id(String prefix) {
        return prefix + "_" + UUID.randomUUID().toString().replace("-", "").toLowerCase();
    }
    public static BpmnModel toBpmn(JsonNode processNode, JsonNode formNode,String formName,String templateId) {
        //表单存储
        JSONObject jsonObject=new JSONObject();
        jsonObject.put("processJson", processNode.toString());
        jsonObject.put("formJson", formNode.toString());


        // 一.准备工作
        BpmnModel bpmnModel = new BpmnModel();

        String id=""+ UUID.randomUUID();


        bpmnModel.setTargetNamespace(id+"");

        ExtensionAttribute extensionAttribute=new ExtensionAttribute();
        extensionAttribute.setName("Junmp");
        extensionAttribute.setNamespace("http://flowable.org/bpmn");
        extensionAttribute.setValue(jsonObject.toJSONString());

        Process process = new Process(); // 相当于图纸
        process.setName(formName);
        process.setId("Process_"+templateId);
        process.setExecutable(true);
        process.addAttribute(extensionAttribute);

        bpmnModel.addProcess(process);
        // 二.开始结束节点
        StartEvent startEvent = new StartEvent();// 新建开始节点
        startEvent.setId(id("start"));
        startEvent.setInitiator("initiator");
        process.addFlowElement(startEvent);// 绘制到图纸
        EndEvent endEvent = new EndEvent(); // 新建结束节点
        endEvent.setId(id("end"));// 绘制到图纸
        process.addFlowElement(endEvent);



        // 三.递归绘制节点
        drawNode("0",process, processNode,formNode, startEvent.getId(), endEvent.getId(), null);
        List<FlowableListener> executionListeners =new ArrayList<>();
        FlowableListener flowableListener=new FlowableListener();
        flowableListener.setEvent(ExecutionListener.EVENTNAME_END);
        flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
        flowableListener.setImplementation("${processListener}");
        executionListeners.add(flowableListener);
        process.setExecutionListeners(executionListeners);
        // 四.自动布局
        new BpmnAutoLayout(bpmnModel).execute();
        // 五.转xml

        return bpmnModel;
    }

    /**
     * 绘制节点
     * @param process bpmn process 图纸
     * @param node    json的节点
     * @param preId   上一节点id
     * @param endId   结束节点
     * @param preExpression 上一节点表达式
     */
    public static void drawNode(String typeNode,Process process, JsonNode node,JsonNode formJson, String preId, String endId, String preExpression) {

        // 根据type绘制不同种类的节点
        Inout inout = drawNodeByType(process, node,formJson,typeNode);
        // 绘制前一根线
        process.addFlowElement(createSequenceFlow(preId, inout.getIn(), preExpression));
        if (node.get("childNode").toString().equals("null")) {
            // 没有下一步, 绘制指向结束的线
            process.addFlowElement(createSequenceFlow(inout.getOut(), endId, null));

        } else {
            // 有下一步, 递归绘制下一个节点
            drawNode("",process, node.get("childNode"),formJson, inout.getOut(), endId, null);
        }
    }

    /**
     * 绘制不同种类节点
     * @param process
     * @param node
     * @return Inout
     */
    private static Inout drawNodeByType(Process process, JsonNode node,JsonNode formJson,String typeNode) {
        if (node.get("type").toString().equals("0")) {  //标准节点
            return drawBzNode(process, node,formJson,typeNode);
        } else if (node.get("type").toString().equals("1")) {//审核节点
            return drawAuditNode(process, node);
        }  else if (node.get("type").toString().equals("4")) {//路由
            return drawExclusiveNode(process, node);
        }
        else {
            throw new IllegalArgumentException();
        }
    }

    /**
     * 绘制标准节点
     * @param process
     * @param node
     * @return Inout
     */
    private static Inout drawBzNode(Process process, JsonNode node,JsonNode formJson,String typeNode) {

        String id="root";
        //创建发起人结点
        UserTask task = new UserTask();
        //设置结点ID
        task.setId(id);
        //设置结点名称
        task.setName(node.get("name").asText());
        //通过将 ${initiator} 作为指派人的值，可以将用户任务分配给流程的发起人。当流程实例启动时，${initiator} 会被替换为实际的发起人信息。
        //需要注意的是，在使用表达式时，需要确保流程引擎的配置中启用了表达式语言（例如使用 Flowable 的默认配置，会启用表达式语言）。
//        task.setAssignee("${initiator}");
        List<FormProperty> formProperties = formJsonToFormProperty(formJson);
        task.setFormProperties(formProperties);
        ArrayList<FlowableListener> taskListeners = new ArrayList<>();
        FlowableListener taskListener = new FlowableListener();
        // 事件类型,
        taskListener.setEvent(TaskListener.EVENTNAME_CREATE);
        // 监听器类型
        taskListener.setImplementationType(IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
        // 设置实现了，这里设置监听器的类型是delegateExpression，这样可以在实现类注入Spring bean.
        taskListener.setImplementation("${taskCreatedListener}");
        taskListeners.add(taskListener);
        task.setTaskListeners(taskListeners);

        //添加连线
        process.addFlowElement(task);

        return new Inout(id, id);
    }

    private static List<FormProperty> formJsonToFormProperty(JsonNode formJson){
        List<FormProperty> formList=new ArrayList<>();


        for (JsonNode listInfo : formJson.get("list")) {
            FormProperty form=new FormProperty();
            JsonNode key=listInfo.get("key");
            form.setId(key.asText());
            form.setName(key.asText());
            formList.add(form);
        }

        return  formList;
    }


    /**
     * 绘制审核节点
     * @param process
     * @param node
     * @return Inout
     */
    private static Inout drawAuditNode(Process process, JsonNode node) {
        // 绘制节点
//        String id = "Node_"+UUID.randomUUID();
        String id ="Node_"+ node.get("id").asText();
        UserTask userTask = new UserTask();
        userTask.setId(id);
        //写入节点名称
        userTask.setName(node.get("name").asText());
        ObjectMapper objectMapper = new ObjectMapper();
        JsonNode[] approverGroups = objectMapper.convertValue(node.get("approverGroups"), JsonNode[].class);
        userTask.setExecutionListeners(getTaskListeners());//角色监听
//        userTask.setExecutionListeners(SkipTaskListeners());//跳过事件监听

        MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = new MultiInstanceLoopCharacteristics();
        // 审批人集合参数
        multiInstanceLoopCharacteristics.setInputDataItem(userTask.getId()+"assigneeList");
        // 迭代集合
        multiInstanceLoopCharacteristics.setElementVariable("assigneeName");
        // 串行
        multiInstanceLoopCharacteristics.setSequential(false);
        multiInstanceLoopCharacteristics.setCompletionCondition("${true}");
        userTask.setAssignee("${assigneeName}");
//         设置多实例属性
        userTask.setLoopCharacteristics(multiInstanceLoopCharacteristics);
        process.addFlowElement(userTask);
        return new Inout(id, id);
    }

    /**
     * 监听任务启动后角色分配事件
     *
     * @return 监听节点列表
     */
    private static List<FlowableListener> getTaskListeners() {
        ArrayList<FlowableListener> taskListeners = new ArrayList<>();

        // 第一个监听器
        FlowableListener counterSignListener = new FlowableListener();
        counterSignListener.setEvent(ExecutionListener.EVENTNAME_START);
        counterSignListener.setImplementationType(IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
        counterSignListener.setImplementation("${counterSignListener}");
        taskListeners.add(counterSignListener);

        // 第二个监听器
        FlowableListener skipTaskListener = new FlowableListener();
        skipTaskListener.setEvent(ExecutionListener.EVENTNAME_START);
        skipTaskListener.setImplementationType(IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
        skipTaskListener.setImplementation("${skipTaskListener}");
        taskListeners.add(skipTaskListener);

        return taskListeners;
    }
    /**
     * 监听任务启动后角色分配事件
     *
     * @return 监听节点列表
     */
    private static List<FlowableListener> SkipTaskListeners() {
        ArrayList<FlowableListener> taskListeners = new ArrayList<>();
        //监听器开始class
        FlowableListener listener = new FlowableListener();
        listener.setEvent(ExecutionListener.EVENTNAME_START);//监听任务启动
        listener.setImplementationType(IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
        listener.setImplementation("${SkipTaskListener}");//启动角色分配监听事件
        taskListeners.add(listener);
        return taskListeners;
    }
    /**
     * 绘制分支节点
     * @param process 绘图对象
     * @param node 当前节点信息
     * @return Inout
     */
    private static Inout drawExclusiveNode(Process process, JsonNode node) {
        // 开始网关
        String startId = "Exclusive_"+UUID.randomUUID();
        ExclusiveGateway startGateway = new ExclusiveGateway();
        startGateway.setId(startId);
        process.addFlowElement(startGateway);
        // 结束网关
        String endId = "Exclusive_"+UUID.randomUUID();
        ExclusiveGateway endGateway = new ExclusiveGateway();
        endGateway.setId(endId);
        process.addFlowElement(endGateway);
        // 绘制分支
        JsonNode branches = node.get("conditionNodes");
        for (JsonNode branch : branches) {
            if (branch.get("childNode").toString().equals("null")) {
                // 没有子流程，直接绘制结束 内容为EL表达式
                //解释JSON数据，转换为EL表达式
                JsonNode conditionGroup=branch.get("conditionGroup");
                String condition = JsonToELConverter.convertToEL(conditionGroup);
                //条件线路 根前端数据对接 内容为EL表达式
                process.addFlowElement(createSequenceFlow(startId, endId, condition));


            } else {
                // 有子流程，递归绘制子流程
                //解释JSON数据，转换为EL表达式
                JsonNode conditionGroup=branch.get("conditionGroup");
                //把JSOn转换为EL表达式
                String condition = JsonToELConverter.convertToEL(conditionGroup);
                //条件线路   根前端数据对接 内容为EL表达式
                drawNode("",process, branch.get("childNode"),null, startId, endId, condition);
            }
        }
        // int和out不一样
        return new Inout(startId, endId);
    }

    /**
     * 创建连线
     * @param from 起点
     * @param to   终点
     * @return SequenceFlow
     */
    public static SequenceFlow createSequenceFlow(String from, String to, String conditionExpression) {
        SequenceFlow flow = new SequenceFlow();
        flow.setId("Node_"+UUID.randomUUID());
        flow.setSourceRef(from);
        flow.setTargetRef(to);
        if (conditionExpression != null) {
            flow.setConditionExpression(conditionExpression);
        }
        return flow;
    }
}