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.repository.Deployment;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.ui.modeler.domain.Model;
import org.flowable.ui.modeler.serviceapi.ModelService;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.RestController;

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

/**
 * @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";

        // 部署流程
        Deployment deploy = repositoryService.createDeployment()
                .name(modelData.getName())
                .key("test")
                .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 启动流程
         runtimeService.startProcessInstanceById(deployId, dataKey);
        log.info("流程启动成功：" + deployId );
        //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) {
        log.info("中止流程,processId:" + processId);
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        runtimeService.deleteProcessInstance(processId, "中止流程");
        return rsp;
    }

    @Override
    public ApiRespPdu<List<String>> getRuntimeDataId() {
        ApiRespPdu<List<String>> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        // 获取正在执行的任务列表
        List<Execution> list = runtimeService.createExecutionQuery().onlyProcessInstanceExecutions().list();
        List<String> idList = list.stream().map(item -> {
            // 根据任务获取流程实例
            // 获取流程实例种的业务 key
            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(item.getProcessInstanceId()).singleResult();
            return pi.getBusinessKey();
        }).collect(Collectors.toList());

        rsp.setData(idList);
        return rsp;
    }

    @Override
    public ApiRespPdu<List<Map<String, Object>>> getRuntimeBusinessKeyByUser(String userId) {
        ApiRespPdu<List<Map<String, Object>>> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        // 根据用户获取正在进行的任务
        List<Task> tasks = taskService.createTaskQuery().taskAssignee(userId).list();

        List<Map<String, Object>> idList = tasks.stream().map(task -> {
            Map<String, Object> data = new HashMap<>();
            // 根据任务获取流程实例
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
            // 获取流程实例中的业务键
            data.put("businessKey", processInstance.getBusinessKey());
            // 获取任务 Id
            data.put("taskId", task.getId());
            // 流程定义名称
            data.put("processInstanceName", processInstance.getProcessDefinitionName());
            // 流程开始时间
            data.put("startTime", processInstance.getStartTime());

            return data;
        }).collect(Collectors.toList());

        log.info("userId :"+userId+"idList:"+JSON.toJSONString(idList));
        rsp.setData(idList);
        return rsp;
    }

    @Override
    public ApiRespPdu<List<Map<String, Object>>> getRuntimeBusinessKeyByGroup(List<String> groupIds) {
        ApiRespPdu<List<Map<String, Object>>> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        if (ObjectUtils.isEmpty(groupIds)) {
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("groupIds is null");
            return rsp;
        }
        // 根据发起人获取正在执行的任务列表
        List<Task> tasks = taskService.createTaskQuery().taskCandidateGroupIn(groupIds).list();
        List<Map<String, Object>> idList = tasks.stream().map(task -> {
            Map<String, Object> data = new HashMap<>();
            // 根据任务获取流程实例
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
            // 获取流程实例中的业务键
            data.put("businessKey", processInstance.getBusinessKey());
            // 获取任务 Id
            data.put("taskId", task.getId());
            // 流程定义名称
            data.put("processInstanceName", processInstance.getProcessDefinitionName());
            // 流程开始时间
            data.put("startTime", processInstance.getStartTime());

            return data;
        }).collect(Collectors.toList());
        rsp.setData(idList);
        return rsp;


    }

    @Override
    public ApiRespPdu<List<Map<String, Object>>> getHistoryByUser(String userId) {
        ApiRespPdu<List<Map<String, Object>>> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        // 根据用户，查询任务实例历史
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().taskAssignee(userId).finished().orderByHistoricTaskInstanceEndTime().desc().list();
        List<Map<String, Object>> historyList = list.stream().map(historicTaskInstance -> {
            // 历史流程实例
            HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery().processInstanceId(historicTaskInstance.getProcessInstanceId()).singleResult();
            // 获取需要的历史数据
            Map<String, Object> historyInfo = new HashMap<>();
            historyInfo.put("assignee", historicTaskInstance.getAssignee());
            // 节点名称
            historyInfo.put("nodeName", historicTaskInstance.getName());
            // 流程开始时间
            historyInfo.put("startTime", historicTaskInstance.getCreateTime());
            // 节点操作时间（本流程节点结束时间）
            historyInfo.put("endTime", historicTaskInstance.getEndTime());
            // 流程定义名称
            historyInfo.put("processName", hpi.getProcessDefinitionName());
            // 流程实例 ID
            historyInfo.put("processInstanceId", historicTaskInstance.getProcessInstanceId());
            // 业务键
            historyInfo.put("businessKey", hpi.getBusinessKey());
            return historyInfo;
        }).collect(Collectors.toList());

        rsp.setData(historyList);

        return rsp;
    }

    @Override
    public ApiRespPdu<Boolean> checkProcessInstanceFinish(String processInstanceId) {
        ApiRespPdu<Boolean> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());

        boolean isFinish = false;
        // 根据流程 ID 获取未完成的流程中是否存在此流程
        long count = historyService.createHistoricProcessInstanceQuery().unfinished().processInstanceId(processInstanceId).count();
        // 不存在说明没有结束
        if (count == 0) {
            isFinish = true;
        }
        rsp.setData(isFinish);
        return rsp;
    }

    @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) {
        log.info("receive taskId:"+taskId);
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        rsp.setMsg(YesNoEnum.YES.getDesc());
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();

        if(!ObjectUtils.isEmpty(task)){
            rsp.setData(task.getProcessInstanceId());
        }else{
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("taskinfo is null taskId:"+taskId);
        }


        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;
    }
}
