package com.mortals.flowable.service.impl;


import com.fasterxml.jackson.databind.node.ObjectNode;
import com.mortals.flowable.common.CustomBpmnJsonConverter;
import com.mortals.flowable.service.IFlowableBpmnService;
import com.mortals.xhx.common.code.YesNoEnum;
import com.mortals.xhx.common.pdu.api.ApiRespPdu;
import org.apache.commons.lang.StringUtils;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.editor.language.json.converter.BaseBpmnJsonConverter;
import org.flowable.editor.language.json.converter.util.CollectionUtils;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.flowable.ui.common.service.exception.BadRequestException;
import org.flowable.ui.common.util.XmlUtil;
import org.flowable.ui.modeler.domain.AbstractModel;
import org.flowable.ui.modeler.domain.Model;
import org.flowable.ui.modeler.model.ModelRepresentation;
import org.flowable.ui.modeler.serviceapi.ModelService;
import org.flowable.validation.ProcessValidator;
import org.flowable.validation.ProcessValidatorFactory;
import org.flowable.validation.ValidationError;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;

/**
 * 流程引擎服务实现
 *
 * @author: zxfei
 * @date: 2021/8/27 1:37
 */
@Service
public class FlowableBpmnServiceImpl implements IFlowableBpmnService {

    private static final String BPMN_EXTENSION = ".bpmn";
    private static final String BPMN20_XML_EXTENSION = ".bpmn20.xml";

    @Autowired
    private ModelService modelService;
    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    protected CustomBpmnJsonConverter bpmnJsonConverter;
    @Autowired
    protected BpmnXMLConverter bpmnXMLConverter;
    @Autowired
    private ProcessValidatorFactory processValidatorFactory;

    @Override
    public ApiRespPdu<String> importBpmnModel(String modelId, String fileName, InputStream modelStream) {
        ApiRespPdu<String> returnVo = new ApiRespPdu(YesNoEnum.YES.getValue(), "OK");
        Model processModel = modelService.getModel(modelId);


        if (StringUtils.isBlank(fileName)) {
            fileName = processModel.getKey() + BPMN_EXTENSION;
        }
        if (fileName != null && (fileName.endsWith(BPMN_EXTENSION) || fileName.endsWith(BPMN20_XML_EXTENSION))) {
            try {
                XMLInputFactory xif = XmlUtil.createSafeXmlInputFactory();

                InputStreamReader xmlIn = new InputStreamReader(modelStream, StandardCharsets.UTF_8);
                XMLStreamReader xtr = xif.createXMLStreamReader(xmlIn);

                BpmnModel bpmnModel = bpmnXMLConverter.convertToBpmnModel(xtr);
                bpmnModel.getMainProcess().setId(processModel.getKey());
                bpmnModel.setTargetNamespace(BaseBpmnJsonConverter.NAMESPACE);
                if (CollectionUtils.isEmpty(bpmnModel.getProcesses())) {
                    returnVo = new ApiRespPdu(YesNoEnum.NO.getValue(), "No process found in definition " + fileName);
                    return returnVo;
                }
                if (bpmnModel.getLocationMap().size() == 0) {
                    returnVo = new ApiRespPdu(YesNoEnum.NO.getValue(), "No required BPMN DI information found in definition " + fileName);
                    return returnVo;
                }
                ProcessValidator processValidator = processValidatorFactory.createDefaultProcessValidator();
                List<ValidationError> validationErrors = processValidator.validate(bpmnModel);
                if (CollectionUtils.isNotEmpty(validationErrors)) {
                    StringBuffer message = new StringBuffer();
                    validationErrors.forEach(validationError -> message.append(validationErrors.toString()));
                    returnVo = new ApiRespPdu(YesNoEnum.NO.getValue(), message.toString());
                    return returnVo;
                }
                ObjectNode modelNode = bpmnJsonConverter.convertToJson(bpmnModel);
                this.setProcessPropertiesToKey(modelNode, processModel.getKey());
                AbstractModel savedModel = modelService.saveModel(modelId, processModel.getName(), processModel.getKey(),
                        processModel.getDescription(), modelNode.toString(), false,
                        null, "admin");
                returnVo.setData(savedModel.getId());
                return returnVo;
            } catch (BadRequestException e) {
                throw e;
            } catch (Exception e) {
                returnVo = new ApiRespPdu(YesNoEnum.NO.getValue(), "bpmn.js failed for " + fileName + ", error message " + e.getMessage());
                return returnVo;
            }
        } else {
            returnVo = new ApiRespPdu(YesNoEnum.NO.getValue(), "Invalid file name, only .bpmn and .bpmn20.xml files are supported not " + fileName);
            return returnVo;
        }
    }

    private void setProcessPropertiesToKey(ObjectNode modelNode, String key) {
        ObjectNode objectNode = (ObjectNode) modelNode.get("properties");
        objectNode.put("process_id", key);
        objectNode.put("processid", key);
    }


}
