package cn.palmte.work.utils;

import cn.palmte.work.config.activiti.ActConstant;
import cn.palmte.work.config.activiti.DeleteTaskCommand;
import cn.palmte.work.config.activiti.JumpCommand;
import cn.palmte.work.model.ProjectInstanceRelation;
import cn.palmte.work.model.ProjectInstanceRelationRepository;
import cn.palmte.work.service.AccountService;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.engine.ManagementService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.IdentityLink;
import org.activiti.engine.task.Task;
import org.activiti.image.ProcessDiagramGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import top.jfunc.common.db.bean.Record;
import top.jfunc.common.db.utils.Pagination;
import top.jfunc.common.utils.IoUtil;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

@Component
public class ActUtil {
    private static final Logger logger = LoggerFactory.getLogger(ActUtil.class);
    @Autowired
    private ProcessEngine processEngine;
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    Pagination pagination;
    @Resource
    private ApplicationContext applicationContext;
    @Autowired
    private TaskService taskService;
    @Autowired
    private AccountService accountService;
    @Autowired
    private ProjectInstanceRelationRepository projectInstanceRelationRepository;


    public Task getCurrentTask(int projectId) {
        try {
            List<ProjectInstanceRelation> relationList = projectInstanceRelationRepository
                    .findByProjectIdOrderByCreateTimeDesc(projectId);
            ProjectInstanceRelation projectInstanceRelation = relationList.get(0);
            return taskService.createTaskQuery().processInstanceId(projectInstanceRelation.getProcessInsId()).singleResult();
        } catch (Exception e) {
            logger.error("", e);
            return null;
        }
    }

    /**
     * 获取流程实例里的变量
     *
     * @param variableName
     * @param procInsId
     * @return
     */
    public Record getVariable(String variableName, String procInsId) {
        String sql = "select TEXT_ as text from ACT_HI_VARINST where NAME_=? and PROC_INST_ID_=?";
        return pagination.findFirst(sql, variableName, procInsId);
    }


    public String getStartUserId(String procInsId) {
        Record record = getVariable(ActConstant.START_PROCESS_USERID, procInsId);
        if (record != null) {
            return record.getStr("text");
        }
        return "0";
    }


    /**
     * 获取流程实列里的所有变量
     *
     * @param procInsId
     * @return
     */
    public List<Record> getVariables(String procInsId) {
        String sql = "select NAME_ as name, TEXT_ as text from ACT_HI_VARINST where PROC_INST_ID_=?";
        return pagination.find(sql, procInsId);
    }



    /**
     * 跳转到指定任务节点
     *
     * @param currentTaskId    当前任务id
     * @param targetTaskDefKey 跳转目的任务key
     */
    public void jumpToTargetTask(String currentTaskId, String targetTaskDefKey) {
        Task currentTask = taskService.createTaskQuery().taskId(currentTaskId).singleResult();
        // 获取流程定义
        org.activiti.bpmn.model.Process process = repositoryService.getBpmnModel(currentTask.getProcessDefinitionId()).getMainProcess();
        //获取目标节点定义
        FlowNode targetNode = (FlowNode) process.getFlowElement(targetTaskDefKey);

        ManagementService managementService = processEngine.getManagementService();
        //删除当前运行任务
        String executionEntityId = managementService.executeCommand(new DeleteTaskCommand(currentTask.getId()));
        //流程执行到来源节点
        managementService.executeCommand(new JumpCommand(targetNode, executionEntityId));

        Task singleResult = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId()).singleResult();
        singleResult.setParentTaskId(currentTask.getTaskDefinitionKey());
        taskService.saveTask(singleResult);
    }

    /**
     * 响应图片
     *
     * @param response
     * @param bpmnModel
     * @param executedActivityIdList
     * @param flowIds
     * @throws IOException
     */
    public void responsePng(HttpServletResponse response, BpmnModel bpmnModel, List<String> executedActivityIdList, List<String> flowIds) throws IOException {
        try (InputStream inputStream = generateDiagramInputStream(bpmnModel, executedActivityIdList, flowIds)) {
            IoUtil.copy(inputStream, response.getOutputStream());
        }
    }


    /**
     * 查看流程xml
     *
     * @param response
     * @param deploymentId
     * @throws IOException
     */
    public void responseXml(HttpServletResponse response, String deploymentId) throws IOException {
        try (InputStream inputStream = getXmlStreamByDeploymentId(deploymentId)) {
            IoUtil.copy(new ByteArrayInputStream("<xmp>".getBytes()), response.getOutputStream());
            IoUtil.copy(inputStream, response.getOutputStream());
            IoUtil.copy(new ByteArrayInputStream("</xmp>".getBytes()), response.getOutputStream());
        }
    }


    /**
     * 查询任务审批人
     *
     * @param taskId
     * @return
     */
    public String getAssigneeByIdentityLink(String taskId) {
        List<IdentityLink> identityLinkList = taskService.getIdentityLinksForTask(taskId);
        if (identityLinkList == null || identityLinkList.isEmpty()) {
            return "";
        }

        StringBuilder namesBuilder = new StringBuilder();
        for (IdentityLink identityLink : identityLinkList) {
            if ("assignee".equals(identityLink.getType()) || "candidate".equals(identityLink.getType())) {
                String assigneeUserId = identityLink.getUserId();
                if (StrKit.isBlank(assigneeUserId) || "null".equals(assigneeUserId)) {
                    continue;
                }
                namesBuilder.append(accountService.getNameById(Integer.parseInt(assigneeUserId)));
                namesBuilder.append(",");
            }
        }
        String names = namesBuilder.toString();
        if (names.endsWith(",")) {
            names = names.substring(0, names.length() - 1);
        }
        return names;
    }

    public List<String> getAssignUserList(String taskId) {
        List<String> userList = new ArrayList<>();
        List<IdentityLink> identityLinkList = taskService.getIdentityLinksForTask(taskId);
        if (identityLinkList == null || identityLinkList.isEmpty()) {
            return userList;
        }

        for (IdentityLink identityLink : identityLinkList) {
            if ("assignee".equals(identityLink.getType()) || "candidate".equals(identityLink.getType())) {
                String assigneeUserId = identityLink.getUserId();
                userList.add(assigneeUserId);
            }
        }
        return userList;
    }


    public static String filterNull(final Object str) {
        String rs = (str == null) ? "" : str.toString().trim();
        return "null".equals(rs) ? "" : rs;
    }

    public static String filterNullToZero(final Object stro) {
        String s = filterNull(stro);
        if ("".equals(s)) {
            s = "0";
        }
        return s;
    }


    /**
     * 生成xml流
     *
     * @param deploymentId
     * @return
     * @throws IOException
     */
    private InputStream getXmlStreamByDeploymentId(String deploymentId) throws IOException {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
        return repositoryService.getResourceAsStream(deploymentId, processDefinition.getResourceName());
    }


    /**
     * 生成流程图片流
     *
     * @param bpmnModel
     * @param executedActivityIdList
     * @param flowIds
     * @return
     */
    private InputStream generateDiagramInputStream(BpmnModel bpmnModel, List<String> executedActivityIdList, List<String> flowIds) {
        try {
            ProcessDiagramGenerator processDiagramGenerator = processEngine.getProcessEngineConfiguration().getProcessDiagramGenerator();
            return processDiagramGenerator.generateDiagram(bpmnModel, "png", executedActivityIdList,
                    flowIds, "宋体", "微软雅黑", "黑体", null, 2.0);    //使用默认配置获得流程图表生成器，并生成追踪图片字符流
        } catch (Exception e) {
            logger.error("an exception happens in try catch statement", e);
            return null;
        }
    }


}
