package com.mortals.flowable.service;

import cn.hutool.core.codec.Base64;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.mortals.flowable.common.CustomBpmnJsonConverter;
import com.mortals.flowable.common.constant.ProcessConstants;
import com.mortals.flowable.common.utils.ElUtils;
import com.mortals.flowable.factory.FlowServiceFactory;
import com.mortals.xhx.common.code.YesNoEnum;
import com.mortals.xhx.common.code.flowable.runtime.StartVariableEnum;
import com.mortals.xhx.common.pdu.api.ApiRespPdu;
import com.mortals.xhx.common.pdu.flow.FlowViewerPdu;
import com.mortals.xhx.feign.flowable.IApiFlowInstanceFeign;
import com.mortals.xhx.feign.flowable.IApiFlowModelFeign;
import com.mortals.xhx.flowable.model.ModelInfoVo;
import com.mortals.xhx.flowable.runtime.StartProcessInstanceReq;
import lombok.extern.apachecommons.CommonsLog;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.BpmnAutoLayout;
import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.StartEvent;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.editor.language.json.converter.util.CollectionUtils;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.idm.api.User;
import org.flowable.ui.common.security.SecurityUtils;
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.repository.ModelRepository;
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.web.bind.annotation.RestController;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 流程模型服务类
 *
 * @author: finegirl
 * @date: 2021/8/2 10:50
 * @description:
 **/
@RestController
@CommonsLog
public class FlowModelServiceImpl extends FlowServiceFactory implements IApiFlowModelFeign {


    @Autowired
    private IFlowableBpmnService flowableBpmnService;
    @Autowired
    private ModelService modelService;

    @Autowired
    protected ModelRepository modelRepository;

    @Autowired
    protected CustomBpmnJsonConverter bpmnJsonConverter;
    @Autowired
    protected BpmnXMLConverter bpmnXmlConverter;

    @Override
    public ApiRespPdu<String> saveBpmnModel(ModelInfoVo modelInfoVo) {
        ApiRespPdu<String> rsp = new ApiRespPdu(YesNoEnum.YES.getValue(), "OK");

        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(modelInfoVo.getModelXml().getBytes());

        ApiRespPdu<String> representationReturnVo = flowableBpmnService.importBpmnModel(modelInfoVo.getModelId(),
                modelInfoVo.getFileName(), byteArrayInputStream);

        if (!representationReturnVo.isSuccess()) {
            rsp = new ApiRespPdu<>(YesNoEnum.NO.getValue(), representationReturnVo.getMsg());
            return rsp;
        } else {
            rsp.setData(representationReturnVo.getData());
        }
        return rsp;
    }

    @Override
    public ApiRespPdu<String> addBpmnModel(ModelInfoVo modelInfoVo) {

        ApiRespPdu<String> rsp = new ApiRespPdu(YesNoEnum.YES.getValue(), "OK");

        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(modelInfoVo.getModelXml().getBytes());

        ApiRespPdu<String> representationReturnVo = this.createModel(byteArrayInputStream);

        if (!representationReturnVo.isSuccess()) {
            rsp = new ApiRespPdu<>(YesNoEnum.NO.getValue(), representationReturnVo.getMsg());
            return rsp;
        } else {
            rsp.setData(representationReturnVo.getData());
        }
        return rsp;

    }


    private ApiRespPdu<String> createModel(InputStream inputStream) {
        ApiRespPdu<String> returnVo = new ApiRespPdu(YesNoEnum.YES.getValue(), "创建模板成功");
        try {
            XMLInputFactory xif = XmlUtil.createSafeXmlInputFactory();
            InputStreamReader xmlIn = new InputStreamReader(inputStream, "UTF-8");
            XMLStreamReader xtr = xif.createXMLStreamReader(xmlIn);
            BpmnModel bpmnModel = bpmnXmlConverter.convertToBpmnModel(xtr);
            //模板验证
            ProcessValidator validator = new ProcessValidatorFactory().createDefaultProcessValidator();
            List<ValidationError> errors = validator.validate(bpmnModel);
            if (org.flowable.editor.language.json.converter.util.CollectionUtils.isNotEmpty(errors)) {
                StringBuffer es = new StringBuffer();
                errors.forEach(ve -> es.append(ve.toString()).append("/n"));
                return new ApiRespPdu(YesNoEnum.NO.getValue(), "模板验证失败，原因: " + es.toString());
            }
            String fileName = bpmnModel.getMainProcess().getName();
            if (org.flowable.editor.language.json.converter.util.CollectionUtils.isEmpty(bpmnModel.getProcesses())) {
                return new ApiRespPdu(YesNoEnum.NO.getValue(), "No process found in definition " + fileName);
            }
            if (bpmnModel.getLocationMap().size() == 0) {
                BpmnAutoLayout bpmnLayout = new BpmnAutoLayout(bpmnModel);
                bpmnLayout.execute();
            }
            ObjectNode modelNode = bpmnJsonConverter.convertToJson(bpmnModel);
            org.flowable.bpmn.model.Process process = bpmnModel.getMainProcess();
            String name = process.getId();
            if (StringUtils.isNotEmpty(process.getName())) {
                name = process.getName();
            }
            String description = process.getDocumentation();
            //查询是否已经存在流程模板
            Model newModel = new Model();
            List<Model> models = modelRepository.findByKeyAndType(process.getId(), AbstractModel.MODEL_TYPE_BPMN);
            if (CollectionUtils.isNotEmpty(models)) {
                Model updateModel = models.get(0);
                newModel.setId(updateModel.getId());
            }


            newModel.setName(name);
            newModel.setKey(process.getId());
            newModel.setModelType(AbstractModel.MODEL_TYPE_BPMN);
            newModel.setCreated(Calendar.getInstance().getTime());
            newModel.setCreatedBy(SecurityUtils.getCurrentUserId());
            newModel.setDescription(description);
            newModel.setModelEditorJson(modelNode.toString());
            newModel.setLastUpdated(Calendar.getInstance().getTime());
            newModel.setLastUpdatedBy(SecurityUtils.getCurrentUserId());
            modelService.createModel(newModel, SecurityUtils.getCurrentUserId());
            return returnVo;
        } catch (BadRequestException e) {
            throw e;
        } catch (Exception e) {
            log.error("Import failed for {}", e);
            returnVo = new ApiRespPdu(YesNoEnum.NO.getValue(), "Import failed for , error message " + e.getMessage());
        }
        return returnVo;
    }
}
