package cn.palmte.work.service;


import cn.palmte.work.bean.ApproveStatusEnum;
import cn.palmte.work.config.activiti.ActConstant;
import cn.palmte.work.model.ActTaskDefRepository;
import cn.palmte.work.pojo.ActProcIns;
import cn.palmte.work.utils.ActUtil;
import cn.palmte.work.utils.InterfaceUtil;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import top.jfunc.common.db.QueryHelper;
import top.jfunc.common.db.bean.Page;
import top.jfunc.common.db.utils.Pagination;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;


@Service
public class ActProcInsService {
    private static final Logger logger = LoggerFactory.getLogger(ActProcInsService.class);
    @Autowired
    private RepositoryService repositoryService; //管理流程定义  与流程定义和部署对象相关的Service
    @Autowired
    private TaskService taskService;            //任务管理 与正在执行的任务管理相关的Service

    @Autowired
    private AccountService accountService;

    @Autowired
    private RuntimeService runtimeService;        //与正在执行的流程实例和执行对象相关的Service(执行管理，包括启动、推进、删除流程实例等操作)

    @Autowired
    Pagination pagination;

    @Autowired
    private ProjectTaskRecordService projectTaskRecordService;

    @Autowired
    private HistoryService historyService;        //历史管理(执行完的数据的管理)

    @Autowired
    private ActTaskDefRepository actTaskDefRepository;

    @Autowired
    private ActUtil actUtil;


    /**
     * 启动流程实列
     *
     * @param procDefKey
     * @param variables
     * @return
     */
    public String startProcessInstance(String procDefKey, String businessKey, Map<String, Object> variables) throws Exception {
        List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().processDefinitionKey(procDefKey).active().orderByProcessDefinitionVersion().desc().list();
        if (list == null || list.isEmpty()) {
            throw new Exception("procDefKey(" + procDefKey + ")未定义");
        }

        //取最新版本的流程定义进行启动流程实列
        ProcessDefinition processDefinition = list.get(0);

        String adminId = InterfaceUtil.getAdminId() + "";
        variables.put(ActConstant.START_PROCESS_USERID, adminId);
        //启动流程
        ProcessInstance instance = runtimeService.startProcessInstanceById(processDefinition.getId(), businessKey, variables);
        logger.info("startProcessInstanceSuccess procInsId:{}, procDefKey:{}, procDefName:{}", instance.getId(), instance.getProcessDefinitionKey(), instance.getProcessDefinitionName());

        String procInsId = instance.getProcessInstanceId();
        Task task = taskService.createTaskQuery().processInstanceId(procInsId).singleResult();
        if (task == null) {
            throw new Exception("procDefKey(" + procDefKey + ")启动异常");
        }

        String taskId = task.getId();
        String comment = "提交" + processDefinition.getName();
        Authentication.setAuthenticatedUserId(adminId);
        taskService.addComment(taskId, procInsId, comment);

        //设置发起人为办理人  然后完成任务 任务转入下一个审批节点
        taskService.setAssignee(taskId, adminId);
        taskService.complete(taskId);

        projectTaskRecordService.saveTaskRecord(Integer.parseInt(instance.getBusinessKey()),
                task, ApproveStatusEnum.APPROVAL_PENDING.getApproveStatus(), ActConstant.TASK_INDEX_FIRST_USER_TASK, comment, "");

        return instance.getId();
    }

    /**
     * 流程实列列表
     *
     * @param searchInfo
     * @param pageNumber
     * @param pageSize
     * @return
     */
    public Page<ActProcIns> list(ConcurrentHashMap<String, String> searchInfo, int pageNumber, int pageSize) {
        String select = "select h.proc_inst_id_ as procInsId,h.proc_def_id_ as procDefId," +
                "h.start_time_ as startTime,h.end_time_ as endTime,p.key_ as procKey," +
                "p.name_ as procName,p.NAME_  as dgrmResourceName,p.version_ as version," +
                " GROUP_CONCAT(t.NAME_) as currentTask, GROUP_CONCAT(t.ID_) as currentTaskId," +
                " pjct.name as projectName";
        QueryHelper queryHelper = new QueryHelper(select, " act_hi_procinst h " +
                "left join ACT_RE_PROCDEF p on h.PROC_DEF_ID_ =p.ID_ " +
                "LEFT JOIN act_ru_task t on t.PROC_INST_ID_=h.proc_inst_id_ " +
                "LEFT JOIN project pjct on pjct.id=h.BUSINESS_KEY_");
        queryHelper.addGroupProperty("h.PROC_INST_ID_");
        queryHelper.addOrderProperty("h.start_time_", false);
        Page<ActProcIns> paginate = pagination.paginate(queryHelper.getSql(), ActProcIns.class, pageNumber, pageSize);
        List<ActProcIns> list = paginate.getList();

        for (ActProcIns ins : list) {
            //查询流程发起人
            String userId = actUtil.getStartUserId(ins.getProcInsId());
            ins.setUser(accountService.getNameById(Integer.parseInt(userId)));

            //查询当前任务审批人
            String currentTaskId = ins.getCurrentTaskId();
            if (StringUtils.isNotBlank(currentTaskId)) {
                String[] split = currentTaskId.split(",");
                String candidateUsers = "";
                for (String taskId : split) {
                    candidateUsers += actUtil.getAssigneeByIdentityLink(taskId) + ",";
                }
                if (candidateUsers.endsWith(",")) {
                    candidateUsers = candidateUsers.substring(0, candidateUsers.length() - 1);
                }
                ins.setCandidateUsers(candidateUsers);
            }
        }

        return paginate;
    }


    public void deleteProcessInstance(String procInsId, String reason) {
        runtimeService.deleteProcessInstance(procInsId, reason);
    }


    public void createProcInsPng(HttpServletResponse response, String procInsId) throws IOException {
        try {
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(procInsId).singleResult();            //获取历史流程实例
            //获取流程中已经执行的节点，按照执行先后顺序排序
            List<HistoricActivityInstance> hai = historyService.createHistoricActivityInstanceQuery().processInstanceId(procInsId).orderByHistoricActivityInstanceStartTime().asc().list();
            // 历史流程节点中
            List<HistoricActivityInstance> newHisActInstanceList = new ArrayList<HistoricActivityInstance>();
            List<HistoricActivityInstance> newHisTaskInstanceList = new ArrayList<HistoricActivityInstance>();
            if (hai != null && hai.size() > 0) {
                for (int i = 0; i < hai.size(); i++) {
                    HistoricActivityInstance historicActivityInstance = hai.get(i);
                    String activityType = historicActivityInstance.getActivityType();
                    if (activityType.equals("startEvent") || activityType.equals("endEvent")) {
                        newHisActInstanceList.add(historicActivityInstance);
                    } else if (activityType.equals("serviceTask") || activityType.equals("userTask") || activityType.equals("exclusiveGateway") || activityType.equals("parallelGateway")) {
                        if (newHisTaskInstanceList.size() > 0) {
                            for (int j = 0; j < newHisTaskInstanceList.size(); j++) {
                                HistoricActivityInstance historicTaskInstance = newHisTaskInstanceList.get(j);
                                if (historicTaskInstance.getActivityId().equals(historicActivityInstance.getActivityId())) {  //如果列表中已包括
                                    newHisTaskInstanceList.clear();
                                    newHisTaskInstanceList.add(historicActivityInstance);
                                    break;
                                } else {
                                    newHisTaskInstanceList.add(historicActivityInstance);
                                    break;
                                }
                            }
                        } else {
                            newHisTaskInstanceList.add(historicActivityInstance);
                        }
                    }
                }
            }
            for (int i = 0; i < newHisActInstanceList.size(); i++) {
                HistoricActivityInstance historicActivityInstance = newHisActInstanceList.get(i);
                newHisTaskInstanceList.add(historicActivityInstance);
            }

            List<String> executedActivityIdList = new ArrayList<String>();                        // 构造已执行的节点ID集合
            for (HistoricActivityInstance activityInstance : newHisTaskInstanceList) {
                executedActivityIdList.add(activityInstance.getActivityId());
            }
            BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId()); // 获取bpmnModel
            List<String> flowIds = this.getExecutedFlows(bpmnModel, newHisTaskInstanceList);                        // 获取流程已发生流转的线ID集合
            actUtil.responsePng(response, bpmnModel, executedActivityIdList, flowIds);
        } catch (Exception e) {
            logger.error("an exception happens in try catch statement", e);
        }

    }

    public List<String> getExecutedFlows(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstances) {
        List<String> flowIdList = new ArrayList<String>();                    //流转线ID集合
        List<FlowNode> historicFlowNodeList = new LinkedList<FlowNode>();    //全部活动实例
        List<HistoricActivityInstance> finishedActivityInstanceList = new LinkedList<HistoricActivityInstance>();    //已完成的历史活动节点
        List list = new ArrayList();

        for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
            historicFlowNodeList.add((FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstance.getActivityId(), true));
            if (historicActivityInstance.getEndTime() != null) {
                finishedActivityInstanceList.add(historicActivityInstance);
            }

        }
        for (int x = 0; x < historicActivityInstances.size(); x++) {
            HistoricActivityInstance historicActivityInstance = historicActivityInstances.get(x);
            String activityType = historicActivityInstance.getActivityType();
            String activityId = historicActivityInstance.getActivityId();
            if (!list.contains(activityId) && ("userTask".equals(activityType)
                    || "serviceTask".equals(activityType)
                    || "endEvent".equals(activityType)
                    || "exclusiveGateway".equals(activityType)
                    || "parallelGateway".equals(activityType))) {
                list.add(activityId);
            }
        }
        /**遍历已完成的活动实例，从每个实例的outgoingFlows中找到已执行的*/
        FlowNode currentFlowNode = null;
        for (HistoricActivityInstance currentActivityInstance : finishedActivityInstanceList) {
            /**获得当前活动对应的节点信息及outgoingFlows信息*/
            currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId(), true);
            List<SequenceFlow> sequenceFlowList = currentFlowNode.getOutgoingFlows();
            /**
             * 遍历outgoingFlows并找到已流转的
             * 满足如下条件任务已流转：
             * 1.当前节点是并行网关或包含网关，则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转
             * 2.当前节点是以上两种类型之外的，通过outgoingFlows查找到的时间最近的流转节点视为有效流转
             */
            FlowNode targetFlowNode = null;
            if ("parallelGateway".equals(currentActivityInstance.getActivityType())
                    || "inclusiveGateway".equals(currentActivityInstance.getActivityType())) {
                for (SequenceFlow sequenceFlow : sequenceFlowList) { //遍历历史活动节点，找到匹配Flow目标节点的
                    targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(sequenceFlow.getTargetRef(), true);
                    if (historicFlowNodeList.contains(targetFlowNode)) {
                        flowIdList.add(sequenceFlow.getId());
                    }
                }
            } else {
                List<Map<String, String>> tempMapList = new LinkedList<Map<String, String>>();
//				for(SequenceFlow sequenceFlow : sequenceFlowList) {	 //遍历历史活动节点，找到匹配Flow目标节点的
                for (int i = 0; i < sequenceFlowList.size(); i++) {  //遍历历史活动节点，找到匹配Flow目标节点的
                    SequenceFlow sequenceFlow = sequenceFlowList.get(i);
                    int taskSeq = list.indexOf(sequenceFlow.getSourceRef()); // 获取当前flow目标节点key在审批顺序
                    String nextTaskKey = "";  // 下一个任务节点
                    String beforeTaskKey = sequenceFlow.getSourceRef();  //上一个任务节点

                    if ((taskSeq + 1) < list.size()) { // 判断下一个任务节点是否存在
                        nextTaskKey = String.valueOf(list.get((taskSeq + 1)));
                    }
                    if (taskSeq == list.size() - 1) {
                        nextTaskKey = String.valueOf(list.get((taskSeq)));
                    }
                    for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
                        if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef()) && sequenceFlow.getSourceRef().equals(beforeTaskKey) && sequenceFlow.getTargetRef().equals(nextTaskKey)) {
                            Map<String, String> map = new HashMap<>();
                            map.put("flowId", sequenceFlow.getId());
                            map.put("activityStartTime", String.valueOf(historicActivityInstance.getStartTime().getTime()));
                            tempMapList.add(map);
                        }
                    }
                }
                String flowId = null;
                for (Map<String, String> map : tempMapList) {
                    flowId = map.get("flowId");
                    flowIdList.add(flowId);
                }

            }
        }
        return flowIdList;
    }
}
