package cn.palmte.work.service;

import cn.palmte.work.config.activiti.ActConstant;
import cn.palmte.work.model.ActTaskDef;
import cn.palmte.work.model.ActTaskDefRepository;
import cn.palmte.work.pojo.ActModel;
import cn.palmte.work.utils.InterfaceUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.*;
import org.activiti.bpmn.model.Process;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.delegate.TaskListener;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import top.jfunc.common.db.QueryHelper;
import top.jfunc.common.db.bean.Page;
import top.jfunc.common.db.utils.Pagination;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;


@Service
public class ActModelService {
    private static final Logger logger = LoggerFactory.getLogger(ActModelService.class);
    @Autowired
    private RepositoryService repositoryService; //管理流程定义  与流程定义和部署对象相关的Service

    @Autowired
    private ActTaskDefRepository actTaskDefRepository;

    @Autowired
    Pagination pagination;


    public Page<ActModel> list(ConcurrentHashMap<String, String> searchInfo, int pageNumber, int pageSize) {
        String select = "a.ID_ as id,a.REV_ as rev,a.NAME_ as modelName,a.KEY_ as procDefKey,a.CREATE_TIME_ as createdTime,a.LAST_UPDATE_TIME_ as lastUpdatedTime";
        QueryHelper queryHelper = new QueryHelper(select, " act_re_model a");
        String name = searchInfo.get("name");
        queryHelper.addCondition(StringUtils.isNotEmpty(name), "(a.NAME_=? or a.KEY_=?)", name, name);
        queryHelper.addOrderProperty("a.LAST_UPDATE_TIME_", false);
        return pagination.paginate(queryHelper.getSql(), ActModel.class, pageNumber, pageSize);
    }

    public void createModel(String processId, String modelName) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();
        ObjectNode editorNode = objectMapper.createObjectNode();
        editorNode.put("id", "canvs");
        editorNode.put("resourceId", "canvs");
        ObjectNode stencilSetNode = objectMapper.createObjectNode();
        stencilSetNode.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");        //命名空间(禁止修改)
        stencilSetNode.put("author", "");                                            //流程节点作者
        editorNode.set("stencilset", stencilSetNode);
        ObjectNode propertiesNode = objectMapper.createObjectNode();
        propertiesNode.put("process_id", processId);                                //流程唯一标识
        propertiesNode.put("process_author", InterfaceUtil.getAdmin().getUserName());                        //流程作者
        propertiesNode.put("name", modelName);                                            //流程名称
        editorNode.set("properties", propertiesNode);

        ObjectNode modelObjectNode = objectMapper.createObjectNode();
        modelObjectNode.put("name", modelName);                    //模型名称
        modelObjectNode.put("revision", 1);                        //模型版本
        modelObjectNode.put("description", "");        //模型描述
        Model modelData = repositoryService.newModel();
        //modelData.setCategory(category);						//模型分类
        modelData.setDeploymentId(null);
        modelData.setKey(processId);
        modelData.setMetaInfo(modelObjectNode.toString());
        modelData.setName(modelName);                            //模型名称
        modelData.setTenantId("");
        modelData.setVersion(1);

        //保存模型,存储数据到表：act_re_model 流程设计模型部署表
        repositoryService.saveModel(modelData);

        repositoryService.addModelEditorSource(modelData.getId(), editorNode.toString().getBytes("utf-8"));//保存资源,存储数据到表：act_ge_bytearray 二进制数据表

    }


    /**
     * 删除模型
     * act_re_model 和  act_ge_bytearray 两张表中相关数据都删除
     *
     * @param modelId
     */
    public void deleteModel(String modelId) {
        repositoryService.deleteModel(modelId);
    }

    /**
     * 部署流程
     *
     * @param modelId
     * @throws Exception
     */
    public void deploy(String modelId) throws Exception {
        Model modelData = repositoryService.getModel(modelId);
        ObjectNode modelNode = (ObjectNode) new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
        byte[] bpmnBytes = null;
        BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
        BpmnModel model = jsonConverter.convertToBpmnModel(modelNode);

        //任务创建后监听器
        ActivitiListener activitiListener = new ActivitiListener();
        activitiListener.setEvent(TaskListener.EVENTNAME_CREATE);
        activitiListener.setImplementation("${actListenerService.create(task)}");
        activitiListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_EXPRESSION);
        List<ActivitiListener> activitiListenerList = new ArrayList<>(1);
        activitiListenerList.add(activitiListener);

        List<ActTaskDef> taskList = new ArrayList<>();
        Process process = model.getMainProcess();
        Collection<FlowElement> flowElements = process.getFlowElements();

        int i = 0;
        ActTaskDef taskDef;
        ActTaskDef first = null;
        for (FlowElement element : flowElements) {
            if (element instanceof UserTask) {
                UserTask userTaskElement = (UserTask) element;

                taskDef = new ActTaskDef();
                taskDef.setTaskName(element.getName());
                taskDef.setTaskKey(element.getId());

                MultiInstanceLoopCharacteristics loopCharacteristics = userTaskElement.getLoopCharacteristics();
                if (loopCharacteristics != null) { //多实列
                    loopCharacteristics.setInputDataItem("${actListenerService.getMultiCandidateUsers(execution)}");
                    loopCharacteristics.setElementVariable("assignee");
                    userTaskElement.setAssignee("${assignee}");

                    taskDef.setTaskType(ActConstant.TASK_TYPE_MULTI);
                }else {
                    userTaskElement.setTaskListeners(activitiListenerList);

                    taskDef.setTaskType(ActConstant.TASK_TYPE_SINGE);
                }

                if (i == 1) {//画流程图时，要保证第一个用户任务一定要先画
                    taskDef.setTaskIndex(ActConstant.TASK_INDEX_FIRST_USER_TASK);
                    first = taskDef;
                }

                taskList.add(taskDef);
            }
            i ++;
        }

        bpmnBytes = new BpmnXMLConverter().convertToXML(model);
        String processName = modelData.getName() + ".bpmn20.xml";
        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment().name(modelData.getName());        //部署名称
        deploymentBuilder.addString(processName, new String(bpmnBytes, "utf-8"));
        Deployment deployment = deploymentBuilder.deploy();    //完成部署

        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();
        for (ActTaskDef actTaskDef : taskList) {
            if (first != null) {
                actTaskDef.setRollbackTaskKey(first.getTaskKey());
            }
            actTaskDef.setProcDefId(processDefinition.getId());
            actTaskDef.setProcDefName(processDefinition.getName());
            actTaskDef.setProcDefKey(processDefinition.getKey());
            actTaskDef.setCreatedTime(new Date());
            actTaskDef.setLastUpdatedTime(new Date());
        }

        actTaskDefRepository.save(taskList);

        logger.info("deploy success: deploymentId:{}, procDefName:{}, procDefKey:{}", deployment.getId(), processDefinition.getName(), processDefinition.getKey());
    }

}
