package com.mortals.xhx.flowable.controller;

import cn.hutool.core.codec.Base64;
import com.alibaba.fastjson.JSON;
import com.mortals.framework.exception.AppException;
import com.mortals.xhx.common.code.YesNoEnum;
import com.mortals.xhx.common.pdu.api.ApiRespPdu;
import com.mortals.xhx.feign.flowable.IApiFlowFeign;
import lombok.extern.apachecommons.CommonsLog;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.flowable.ui.modeler.domain.Model;
import org.flowable.ui.modeler.serviceapi.ModelService;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author: finegirl
 * @date: 2021/7/29 14:00
 * @description: 工作流外部调用
 **/
@RestController
@CommonsLog
public class ApiFlowImpl implements IApiFlowFeign {

    // 流程引擎
    @Resource
    private ProcessEngine processEngine;

    // 部署服务
    @Resource
    private RepositoryService repositoryService;

    // 流程节点任务服务
    @Resource
    private TaskService taskService;

    // 模型服务
    @Resource
    private ModelService modelService;

    // 用户以及组管理服务
    @Resource
    private IdentityService identityService;

    // 流程实例服务
    @Resource
    private RuntimeService runtimeService;

    // 历史数据服务
    @Resource
    private HistoryService historyService;

    @Override
    public ApiRespPdu<String> deploy(String modelId) throws AppException {

        log.info("流程部署！modelId:" + modelId);

        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        rsp.setMsg(YesNoEnum.YES.getDesc());

        // 根据模型 ID 获取模型
        Model modelData = modelService.getModel(modelId);

        byte[] bytes = modelService.getBpmnXML(modelData);
        if (bytes == null) {
            log.error("模型数据为空，请先设计流程并成功保存，再进行发布");
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg(YesNoEnum.NO.getDesc());
            rsp.setData("模型数据为空，请先设计流程并成功保存，再进行发布!");
        }

        BpmnModel model = modelService.getBpmnModel(modelData);
        if (model.getProcesses().size() == 0) {
            log.error("数据模型不符要求，请至少设计一条主线流程");
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg(YesNoEnum.NO.getDesc());
            rsp.setData("数据模型不符要求，请至少设计一条主线流程!");
        }
        byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);
        String processName = modelData.getName() + ".bpmn20.xml";

        // 部署流程
        repositoryService.createDeployment()
                .name(modelData.getName())
                .addBytes(processName, bpmnBytes)
                .deploy();

        log.info("流程部署成功：" + modelId + " " + new Date());

        rsp.setData("流程部署成功：" + modelId + " " + new Date());

        return rsp;
    }

    @Override
    public ApiRespPdu<String> start(String deployId, String userId, String dataKey) throws AppException {
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        rsp.setMsg(YesNoEnum.YES.getDesc());
        // 设置发起人
        identityService.setAuthenticatedUserId(userId);
        // 根据流程 ID 启动流程
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(deployId, dataKey);
        log.info("流程启动成功：" + deployId + " processInstance:" + JSON.toJSONString(processInstance));
        rsp.setData(JSON.toJSONString(processInstance));
        return rsp;
    }

    @Override
    public ApiRespPdu<String> taskByAssignee(String taskId, String assignee, Map<String, Object> map) {
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        rsp.setMsg(YesNoEnum.YES.getDesc());

        // 设置审核人
        taskService.setAssignee(taskId, assignee);

        // 设置任务参数，也可不设置：key value，只是示例
        // 带 Local 为局部参数，只适用于本任务，不带 Local 为全局任务，可在其他任务调用参数
        taskService.setVariableLocal(taskId, "status", true);

        // 完成任务
        taskService.complete(taskId, map);
        log.info("任务完成：" + taskId + " " + new Date());

        return rsp;
    }

    @Override
    public ApiRespPdu<String> deleteProcess(String processId) {
        return null;
    }

    @Override
    public ApiRespPdu<List<String>> getRuntimeDataId() {
        return null;
    }

    @Override
    public ApiRespPdu<List<Map<String, Object>>> getRuntimeBusinessKeyByUser(String userId) {
        return null;
    }

    @Override
    public ApiRespPdu<List<Map<String, Object>>> getRuntimeBusinessKeyByGroup(List<String> groupIds) {
        return null;
    }

    @Override
    public ApiRespPdu<List<Map<String, Object>>> getHistoryByUser(String userId) {
        return null;
    }

    @Override
    public ApiRespPdu<Boolean> checkProcessInstanceFinish(String processInstanceId) {
        return null;
    }

    @Override
    public ApiRespPdu<String> setVariables(String taskId, Map<String, Object> map) {
        log.info("设置任务参数：" + taskId + " map:" + JSON.toJSONString(map));
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        rsp.setMsg(YesNoEnum.YES.getDesc());
        String processInstanceId = taskService.createTaskQuery().taskId(taskId).singleResult().getProcessInstanceId();
        runtimeService.setVariables(processInstanceId, map);

        return rsp;
    }

    @Override
    public ApiRespPdu<String> setVariable(String taskId, String key, Object value) {
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        rsp.setMsg(YesNoEnum.YES.getDesc());
        String processInstanceId = taskService.createTaskQuery().taskId(taskId).singleResult().getProcessInstanceId();
        runtimeService.setVariable(processInstanceId, key, value);
        return rsp;
    }

    @Override
    public ApiRespPdu<String> setListVariable(String taskId, String key, List<String> list) {
        log.info(String.format("设置任务参数：%s ,key:%s,list:%s", taskId, key, JSON.toJSONString(list)));
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        rsp.setMsg(YesNoEnum.YES.getDesc());
        String processInstanceId = taskService.createTaskQuery().taskId(taskId).singleResult().getProcessInstanceId();
        runtimeService.setVariable(processInstanceId, key, list);
        return rsp;
    }

    @Override
    public ApiRespPdu<String> task(String taskId) {
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        Boolean isSuspended = taskService.createTaskQuery().taskId(taskId).singleResult().isSuspended();
        if (isSuspended) {
            log.info("任务已挂起，无法完成");
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setData("任务已挂起，无法完成");
            return rsp;
        }
        // 设置任务参数，也可不设置：key value
        // 带 Local 为局部参数，只适用于本任务，不带 Local 为全局任务，可在其他任务调用参数
        taskService.setVariableLocal(taskId, "status", true);
        // 完成任务
        taskService.complete(taskId);
        log.info("任务完成：" + taskId + " " + new Date());
        return rsp;
    }

    @Override
    public ApiRespPdu<String> getTaskInfo(String taskId) {
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        rsp.setMsg(YesNoEnum.YES.getDesc());
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        rsp.setData(task.getProcessInstanceId());

        return rsp;
    }

    @Override
    public String getProcessDiagram(String processInstanceId) throws AppException {
        // 流程定义 ID
        String processDefinitionId;

        // 查看完成的进程中是否存在此进程
        long count = historyService.createHistoricProcessInstanceQuery().finished().processInstanceId(processInstanceId).count();
        if (count > 0) {
            // 如果流程已经结束，则得到结束节点
            log.info("流程已经结束，则得到结束节点！");
            HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();

            processDefinitionId = pi.getProcessDefinitionId();
        } else {// 如果流程没有结束，则取当前活动节点
            log.info("流程没有结束，则取当前活动节点！");
            // 根据流程实例ID获得当前处于活动状态的ActivityId合集
            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            processDefinitionId = pi.getProcessDefinitionId();
        }
        List<String> highLightedActivitis = new ArrayList<>();

        // 获得活动的节点
        List<HistoricActivityInstance> highLightedActivitList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list();

        for (HistoricActivityInstance tempActivity : highLightedActivitList) {
            String activityId = tempActivity.getActivityId();
            highLightedActivitis.add(activityId);
        }

        List<String> flows = new ArrayList<>();
        // 获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        ProcessEngineConfiguration processEngineConfig = processEngine.getProcessEngineConfiguration();

        ProcessDiagramGenerator diagramGenerator = processEngineConfig.getProcessDiagramGenerator();
        InputStream in = diagramGenerator.generateDiagram(bpmnModel, "bmp", highLightedActivitis, flows, processEngineConfig.getActivityFontName(),
                processEngineConfig.getLabelFontName(), processEngineConfig.getAnnotationFontName(), processEngineConfig.getClassLoader(), 1.0, true);
        //通过流生成图片并转化为base64
        return Base64.encode(in);

    }

    @Override
    public ApiRespPdu<String> getTaskProcessDiagram(String taskId) {
        return null;
    }
}
