package com.mortals.flowable.service;

import cn.hutool.core.codec.Base64;
import com.alibaba.fastjson.JSON;
import com.mortals.flowable.common.constant.ProcessConstants;
import com.mortals.flowable.common.enums.FlowComment;
import com.mortals.flowable.factory.FlowServiceFactory;
import com.mortals.framework.model.PageInfo;
import com.mortals.framework.model.Result;
import com.mortals.xhx.common.code.YesNoEnum;
import com.mortals.xhx.common.pdu.api.ApiRespPdu;
import com.mortals.xhx.common.pdu.flow.FlowProcDefPdu;
import com.mortals.xhx.common.pdu.flow.FlowTaskVoPdu;
import com.mortals.xhx.feign.flowable.IApiFlowDefinitionFeign;
import com.mortals.xhx.flowable.FlowDefSaveReq;
import lombok.extern.apachecommons.CommonsLog;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.impl.DefaultProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.springframework.beans.BeanUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 流程定义服务类
 *
 * @author: finegirl
 * @date: 2021/8/2 10:50
 * @description:
 **/
@RestController
@CommonsLog
public class FlowDefinitionServiceImpl extends FlowServiceFactory implements IApiFlowDefinitionFeign {

    private static final String BPMN_FILE_SUFFIX = ".bpmn";

    @Resource
    private IBpmnModelService bpmnModelService;

    @Override
    public ApiRespPdu<Result<FlowProcDefPdu>> list(String tenantId, PageInfo pageInfo) {

        ApiRespPdu<Result<FlowProcDefPdu>> rsp = new ApiRespPdu<>();
        Result<FlowProcDefPdu> result=new Result<>();
        rsp.setCode(YesNoEnum.YES.getValue());

        // 流程定义列表数据查询
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery()
                .processDefinitionTenantId(tenantId)
                .orderByProcessDefinitionId().desc();
        Long totalCount = processDefinitionQuery.count();
        pageInfo.setTotalResult(totalCount.intValue());

        List<ProcessDefinition> processDefinitionList = processDefinitionQuery.listPage(pageInfo.getCurrPage()-1, pageInfo.getPrePageResult());

        List<FlowProcDefPdu> dataList = new ArrayList<>();
        for (ProcessDefinition processDefinition : processDefinitionList) {
            String deploymentId = processDefinition.getDeploymentId();
            Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
            FlowProcDefPdu reProcDef = new FlowProcDefPdu();
            BeanUtils.copyProperties(processDefinition, reProcDef);
            reProcDef.setName(processDefinition.getName());
            reProcDef.setKey(processDefinition.getKey());
            reProcDef.setVersion(processDefinition.getVersion());
            // 流程定义时间
            reProcDef.setDeploymentTime(deployment.getDeploymentTime());

            List<UserTask> datas = bpmnModelService.findUserTasksByProcessDefId(processDefinition.getId());

            List<UserTask> userTasks = new ArrayList<>();

            List<FlowTaskVoPdu> flowTaskPdus = new ArrayList<>();
            if (CollectionUtils.isNotEmpty(datas)) {
                //获取所有审核带el表达式
                userTasks = datas.stream().filter(userTask -> {
                    boolean isAssignee = StringUtils.indexOf(userTask.getAssignee(), "${") != -1;
                    boolean isCandi = false;
                    long count = userTask.getCandidateUsers().stream().filter(item -> StringUtils.indexOf(item, "${") != -1).count();
                    if (count > 0) {
                        isCandi = true;
                    }
                    boolean isGroup = false;
                    long count1 = userTask.getCandidateGroups().stream().filter(item -> StringUtils.indexOf(item, "${") != -1).count();
                    if (count1 > 0) {
                        isGroup = true;
                    }
                    return isAssignee || isCandi || isGroup;

                }).collect(Collectors.toList());
            }
            userTasks.forEach(userTask -> {
                FlowTaskVoPdu flowTaskPdu = new FlowTaskVoPdu();
                flowTaskPdu.setTargetKey(userTask.getId());
                flowTaskPdu.setName(userTask.getName());
                if (userTask.getLoopCharacteristics() != null) {
                    String inputDataItem = userTask.getLoopCharacteristics().getInputDataItem();
                    if (StringUtils.isNotBlank(inputDataItem)) {
                        flowTaskPdu.setMultiple(YesNoEnum.YES.getValue());
                        //并行表达式
                        flowTaskPdu.setEl(inputDataItem);
                    }
                } else {
                    flowTaskPdu.setMultiple(YesNoEnum.NO.getValue());

                    flowTaskPdu.setAssignee(userTask.getAssignee());
                    flowTaskPdu.setCandidateUsers(userTask.getCandidateUsers());
                    flowTaskPdu.setCandidateGroups(userTask.getCandidateGroups());
                    flowTaskPdu.setEl(userTask.getAssignee());
                }
                flowTaskPdus.add(flowTaskPdu);

            });
            reProcDef.setFlowTaskPdus(flowTaskPdus);
            dataList.add(reProcDef);
        }

        result.setList(dataList);
        result.setPageInfo(pageInfo);
        rsp.setData(result);

        log.info(String.format("查询流程列表！resp:%s", JSON.toJSONString(rsp)));

        return rsp;
    }

    @Override
    public ApiRespPdu<String> save(FlowDefSaveReq req) {
        log.info(String.format("保存bpmnXml！name:%s,category:%s", req.getName(), req.getCategory()));
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        try {

            Deployment deploy = repositoryService.createDeployment()
                    .addString(req.getName() + BPMN_FILE_SUFFIX, req.getBpmnXml())
                    .name(req.getName())
                    .category(req.getCategory())
                    .tenantId(req.getTenantId())
                    .deploy();

            ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult();
            repositoryService.setProcessDefinitionCategory(definition.getId(), req.getCategory());
            log.info("proDefId:" + definition.getId());
            rsp.setData(definition.getId());
        } catch (Exception e) {
            log.error("导入bpmnXml错误：", e);
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("导入bpmnXml错误:" + e.getMessage());
        }
        return rsp;
    }

    @Override
    public ApiRespPdu<String> start(String deployId,String tenantId, String userId, String dataKey, Map<String, Object> variables) {
        log.info(String.format("流程实例启动！deployId:%s,userId:%s,dataKey:%s,variables:%s", deployId, userId, dataKey, JSON.toJSONString(variables)));
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        try {
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionId(deployId)
                    .processDefinitionTenantId(tenantId)
                    .latestVersion().singleResult();
            if (!ObjectUtils.isEmpty(processDefinition) && processDefinition.isSuspended()) {
                rsp.setCode(YesNoEnum.NO.getValue());
                rsp.setMsg("流程已被挂起,请先激活流程");
                return rsp;

            }
//           variables.put("skip", true);
//           variables.put(ProcessConstants.FLOWABLE_SKIP_EXPRESSION_ENABLED, true);
            // 设置流程发起人Id到流程中
            identityService.setAuthenticatedUserId(userId);
            variables.put(ProcessConstants.PROCESS_INITIATOR, "");
            ProcessInstance processInstance = runtimeService.startProcessInstanceById(deployId, dataKey, variables);
            // 给第一步申请人节点设置任务执行人和意见 todo:第一个节点不设置为申请人节点有点问题？
//            Task task = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).singleResult();
//            if (Objects.nonNull(task)) {
//                taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), FlowComment.NORMAL.getType(), userId + "发起流程申请");
////                taskService.setAssignee(task.getId(), sysUser.getUserId().toString());
//                taskService.complete(task.getId(), variables);
//            }
            rsp.setMsg("流程启动成功!");
            rsp.setData(processInstance.getId());
            return rsp;
        }catch (Exception e){
            log.error("启动异常",e);
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("启动异常，message:"+e.getMessage());
            return rsp;
        }
    }

    @Override
    public ApiRespPdu<String> updateState(Integer state, String deployId,String tenantId) {
        log.info(String.format("激活或挂起流程定义！deployId:%s,state:%s", deployId, state));
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        try {
            ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery().processDefinitionTenantId(tenantId).deploymentId(deployId).singleResult();
            // 激活
            if (state == 1) {
                repositoryService.activateProcessDefinitionById(procDef.getId(), true, null);
            }
            // 挂起
            if (state == 2) {
                repositoryService.suspendProcessDefinitionById(procDef.getId(), true, null);
            }
        } catch (Exception e) {
            log.error("激活或挂起流程定义异常：", e);
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("激活或挂起流程定义错误:" + e.getMessage());
        }
        return rsp;
    }

    @Override
    public ApiRespPdu<String> delete(String deployId) {
        log.info(String.format("删除流程定义！deployId:%s", deployId));
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());

        try {
            // true 允许级联删除 ,不设置会导致数据库外键关联异常
            repositoryService.deleteDeployment(deployId, true);
        } catch (Exception e) {
            log.error("删除流程定义：", e);
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("删除流程定义:" + e.getMessage());
        }

        return rsp;
    }

    @Override
    public ApiRespPdu<String> readXml(String deployId) {
        log.info(String.format("读取xml！deployId:%s", deployId));
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        try {
            ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult();
            InputStream inputStream = repositoryService.getResourceAsStream(definition.getDeploymentId(), definition.getResourceName());
            String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
            rsp.setData(result);
        } catch (Exception e) {
            log.error("读取xml：", e);
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("读取xml:" + e.getMessage());
        }

        return rsp;
    }

    @Override
    public ApiRespPdu<String> readImage(String deployId) {
        log.info(String.format("读取图片！deployId:%s", deployId));
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        try {
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult();
            //获得图片流
            DefaultProcessDiagramGenerator diagramGenerator = new DefaultProcessDiagramGenerator();
            BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
            //输出为图片
            InputStream inputStream = diagramGenerator.generateDiagram(
                    bpmnModel,
                    "png",
                    Collections.emptyList(),
                    Collections.emptyList(),
                    "宋体",
                    "宋体",
                    "宋体",
                    null,
                    1.0,
                    false);
            //图片转换为base64
            String encodeBase64 = Base64.encode(inputStream);
            rsp.setData(encodeBase64);
        } catch (Exception e) {
            log.error("读取图片：", e);
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("读取图片:" + e.getMessage());
        }
        return rsp;
    }
}
