package org.jeecg.modules.activiti.service.impl;


import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Lists;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.repository.*;
import org.activiti.engine.runtime.ProcessInstance;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.modules.activiti.mapper.ReProcdefMapper;
import org.jeecg.modules.activiti.model.entity.ReProcdefEntity;
import org.jeecg.modules.activiti.service.ReProcdefService;
import org.jeecg.common.Constant;
import org.jeecg.common.exception.CustomException;
import org.jeecg.common.exception.ExceptionCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;

/**
 * 流程实现类
 *
 * @author yao
 * @date 2019/06/12
 */
@Service("reProcdefService")
public class ReProcdefServiceImpl extends ServiceImpl<ReProcdefMapper, ReProcdefEntity> implements ReProcdefService {

    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private RepositoryService repositoryService;

    @Override
    public InputStream resourceRead(String id, String proInsId, String resType) {
        if (StringUtils.isBlank(id)) {
            ProcessInstance processInstance =
                    runtimeService.createProcessInstanceQuery().processInstanceId(proInsId).singleResult();
            id = processInstance.getProcessDefinitionId();
        }
        ProcessDefinition processDefinition =
                repositoryService.createProcessDefinitionQuery().processDefinitionId(id).singleResult();

        String resourceName = "";
        if (Constant.ACT_IMAGE.equals(resType)) {
            resourceName = processDefinition.getDiagramResourceName();
        } else if (Constant.ACT_XML.equals(resType)) {
            resourceName = processDefinition.getResourceName();
        }

        InputStream resourceAsStream =
                repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), resourceName);
        return resourceAsStream;
    }

    @Override
    public void startProcessInstanceById(String processDefinitionId) {
        runtimeService.startProcessInstanceById(processDefinitionId);
    }

    @Override
    public IPage queryPage( Integer pageNo, Integer pageSize,HttpServletRequest req) {

//        String tenantId = req.getHeader("tenant-id");
//         .processDefinitionTenantId(tenantId)
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery()
                .latestVersion()
                .orderByProcessDefinitionKey()
                .asc();
        //processDefinitionQuery.processDefinitionTenantId(UserUtils.getTenantId());
        String[] categories = req.getParameterMap().get("category");
        if (!"".equals(categories) && categories !=null) {
            String category = categories[0];
            processDefinitionQuery.processDefinitionCategory(category);
        }

        String[] keys = req.getParameterMap().get("key");
        if (!"".equals(keys) && keys != null ) {
            String key = keys[0];
            processDefinitionQuery.processDefinitionKey(key);
        }
        List<ProcessDefinition> processDefinitionList = processDefinitionQuery.listPage((pageNo - 1) * pageSize, pageSize);

        List<ReProcdefEntity> list = this.definitionToReprocess(processDefinitionList);

        IPage<ReProcdefEntity> page = new Page<>(pageNo, pageSize);
        page.setTotal((int) processDefinitionQuery.count());
        return page.setRecords(list);
    }

    @Override
    public void delete(String id) {
        repositoryService.deleteDeployment(id, false);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteBatch(String[] deploymentIds) {
        for (String deploymentId : deploymentIds) {
            repositoryService.deleteDeployment(deploymentId, false);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteByDeploymentId(String deploymentId) {
            repositoryService.deleteDeployment(deploymentId, false);

    }

    @Override
    public String deploy(String exportDir, MultipartFile file,HttpServletRequest request) throws IOException {

        String tenantId = request.getHeader("tenant-id");
        StringBuilder message = new StringBuilder();
        String fileName = file.getOriginalFilename();
        InputStream fileInputStream = file.getInputStream();
        Deployment deployment = null;
        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
        deploymentBuilder.tenantId(tenantId);
        String extension = FilenameUtils.getExtension(fileName);
        if (Constant.ACT_ZIP.equals(extension) || Constant.ACT_BAR.equals(extension)) {
            ZipInputStream zip = new ZipInputStream(fileInputStream);
            deployment = deploymentBuilder.addZipInputStream(zip).deploy();
        } else if (Constant.ACT_PNG.equals(extension)) {
            deployment = deploymentBuilder.addInputStream(fileName, fileInputStream).deploy();
        } else if (fileName.indexOf(Constant.ACT_BPMN20) != -1) {
            deployment = deploymentBuilder.addInputStream(fileName, fileInputStream).deploy();
        } else if (Constant.ACT_BPMN.equals(extension)) {
            deployment = deploymentBuilder.addInputStream(fileName, fileInputStream).deploy();
        } else {
            throw new CustomException(ExceptionCode.FILE_TYPE_ERROR);
        }

        List<ProcessDefinition> list =
                repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).list();

        // 设置流程分类
        for (ProcessDefinition processDefinition : list) {
            repositoryService.setProcessDefinitionCategory(
               processDefinition.getId(),
               processDefinition.getCategory());
            message.append("部署成功，流程ID:").append(processDefinition.getId());
        }

        if (list.size() == 0) {
            throw new CustomException(ExceptionCode.ACTIVITI_DESIGN_ERROR);
        }
        return message.toString();
    }

    @Override
    public Model convertToModel(String id,HttpServletRequest request) throws UnsupportedEncodingException, XMLStreamException {
        String tenantId = request.getHeader("tenant-id");
        ProcessDefinition processDefinition =
                repositoryService.createProcessDefinitionQuery().processDefinitionId(id).singleResult();
        InputStream bpmnStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
                processDefinition.getResourceName());
        XMLInputFactory xif = XMLInputFactory.newInstance();
        InputStreamReader in = new InputStreamReader(bpmnStream, "UTF-8");
        XMLStreamReader xtr = xif.createXMLStreamReader(in);
        BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);

        BpmnJsonConverter converter = new BpmnJsonConverter();
        ObjectNode modelNode = converter.convertToJson(bpmnModel);
        Model modelData = repositoryService.newModel();
        modelData.setKey(processDefinition.getKey());
        modelData.setName(processDefinition.getResourceName());
        modelData.setCategory(processDefinition.getCategory());
        modelData.setDeploymentId(processDefinition.getDeploymentId());
        modelData.setVersion(Integer
                .parseInt(String.valueOf(repositoryService.createModelQuery().modelKey(modelData.getKey()).count() + 1)));

        ObjectNode modelObjectNode = new ObjectMapper().createObjectNode();
        modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, processDefinition.getName());
        modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, modelData.getVersion());
        modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, processDefinition.getDescription());
        modelData.setMetaInfo(modelObjectNode.toString());
        modelData.setTenantId(tenantId);
        repositoryService.saveModel(modelData);

        repositoryService.addModelEditorSource(modelData.getId(), modelNode.toString().getBytes("utf-8"));
        return modelData;
    }

    @Override
    public String updateState(int state, String id) {
        String msg = "无操作";
        if (state == Constant.ACT_ONE) {
            try {
                repositoryService.activateProcessDefinitionById(id, true, null);
            } catch (Exception e) {
                throw new CustomException(ExceptionCode.ACTIVITI_ACTIVATED);
            }
            msg = "已激活ID为[" + id + "]的流程定义。";
        } else if (state == Constant.ACT_TWO) {
            try {
                repositoryService.suspendProcessDefinitionById(id, true, null);
            } catch (Exception e) {
                throw new CustomException(ExceptionCode.ACTIVITI_HANG_UP);
            }
            msg = "已挂起ID为[" + id + "]的流程定义。";
        }
        return msg;
    }

    @Override
    public List<ReProcdefEntity> listActive(HttpServletRequest request) {
//        String tenantId = request.getHeader("tenant-id");
//        .processDefinitionTenantId(tenantId)
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery()
                .latestVersion()
                .orderByProcessDefinitionKey()
                .asc();
        List<ProcessDefinition> processDefinitionList = processDefinitionQuery.active().list();
        List<ReProcdefEntity> list = this.definitionToReprocess(processDefinitionList);
        return list;
    }

    /**
     * activti 流程转化成自定义流程
     *
     * @param processDefinitionList
     * @return
     */
    private List<ReProcdefEntity> definitionToReprocess(List<ProcessDefinition> processDefinitionList) {
        List<ReProcdefEntity> list = Lists.newArrayList();
        for (ProcessDefinition processDefinition : processDefinitionList) {
            ReProcdefEntity entity = new ReProcdefEntity();
            String deploymentId = processDefinition.getDeploymentId();
            Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
            entity.setId(processDefinition.getId());
            entity.setKey(processDefinition.getKey());
            entity.setName(deployment == null ? ""
                    : StringUtils.isBlank(deployment.getName()) ? processDefinition.getName() : deployment.getName());
            entity.setDeployTime(deployment == null ? null : deployment.getDeploymentTime());
            entity.setDeploymentId(processDefinition.getDeploymentId());
            entity.setSuspensionState(processDefinition.isSuspended() ? Constant.ACT_TWO : Constant.ACT_ONE);
            entity.setResourceName(processDefinition.getResourceName());
            entity.setDgrmResourceName(processDefinition.getDiagramResourceName());
            entity.setCategory(processDefinition.getCategory());
            entity.setVersion(processDefinition.getVersion());
            entity.setDescription(processDefinition.getDescription());
            entity.setEngineVersion(processDefinition.getVersion() + "");
            entity.setTenantId(processDefinition.getTenantId());
            list.add(entity);
        }
        return list;
    }

}
