package com.casic.missiles.service.listeners.flowable;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.toolkit.SqlRunner;
import com.casic.missiles.enums.system.AssigneeSetTypeEnum;
import com.casic.missiles.enums.system.AssigneeTypeEnum;
import com.casic.missiles.mapper.system.SystemFlowFormMapper;
import com.casic.missiles.model.system.SystemFlowForm;
import com.casic.missiles.modular.system.dao.DeptMapper;
import com.casic.missiles.modular.system.dao.RuRelationMapper;
import com.casic.missiles.modular.system.dao.UserMapper;
import com.casic.missiles.modular.system.model.Dept;
import com.casic.missiles.modular.system.model.RuRelation;
import com.casic.missiles.modular.system.model.User;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;
import org.flowable.engine.repository.ProcessDefinition;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

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

/**
 * @Description: 会签监听器
 *      在流程的执行过程中遇到业务逻辑无法用常规进行的业务的时候可以借助流程监听来实现业务功能
 *      一般来说，我们在事件和网关上使用ExecutionListener，在UserTask上使用TaskListener
 *      ExecutionListener触发时机（start开始、take启用、end结束）（在代码相应位置设置该监听器）：
 *          流程实例的启动和结束。
 *          选中一条连线。
 *          节点的开始和结束。
 *          网关的开始和结束。
 *          中间事件的开始和结束。
 *          开始事件结束或结束事件开始。
 * @Author: wangpeng
 * @Date: 2022/12/27 9:54
 */
@Slf4j
@Component
public class CounterSignListener implements ExecutionListener {
    @Resource
    private RepositoryService repositoryService;
    @Resource
    private SystemFlowFormMapper flowFormMapper;
    @Resource
    private RuRelationMapper ruRelationMapper;
    @Resource
    private UserMapper userMapper;
    @Resource
    private DeptMapper deptMapper;

    //每个任务节点的开始就会执行，在userTask中设置该监听器，保证审批人设置成功
    @Override
    public void notify(DelegateExecution execution) {
        //设置当前任务节点的任务办理人
        //当前节点id，在任务中为任务id
        String taskId = execution.getCurrentActivityId();
        String variable = taskId + "assigneeList";
        //当前任务审批人信息
        List usersValue = (List) execution.getVariable(variable);
        String processDefinitionId = execution.getProcessDefinitionId();
        List<String> assigneeList = new ArrayList<>();
        if (CollectionUtils.isEmpty(usersValue)) {
            //获取当前任务所在节点的审批人列表，vue3每个节点的id是通过id("xxx")方法生成的，vue2中前端传id字段
            //根据任务id获取到任务节点的审批人(已经在生成id之后设置到json)
            ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId);
            QueryWrapper<SystemFlowForm> wrapper = new QueryWrapper<>();
            wrapper.eq("pro_def_id", processDefinition.getKey());
            wrapper.eq("is_del", 0);
            SystemFlowForm systemFlowForm = flowFormMapper.selectOne(wrapper);
            JSONObject jsonObject = JSONObject.parseObject(systemFlowForm.getFlowDef());
            JSONObject nodeConfig = jsonObject.getJSONObject("nodeConfig");
            JSONObject nodeUserJsonObject = getNodeUserList(nodeConfig, taskId);
            if (Objects.isNull(nodeUserJsonObject)) {
                log.error("监听器查找审批人失败，该任务不存在，flowDef json：{}", systemFlowForm.getFlowDef());
                return;
            }

            Integer settype = nodeUserJsonObject.getInteger("settype");
            if(!Objects.isNull(settype)){
                //根据节点不同审批人类型分别进行处理
                if (AssigneeSetTypeEnum.ASSIGN_USER == settype || AssigneeSetTypeEnum.ROLE == settype) {
                    JSONArray nodeUserArray = nodeUserJsonObject.getJSONArray("nodeUserList");
                    //所选审批人分类
                    Iterator<Object> iterator = nodeUserArray.stream().iterator();
                    while (iterator.hasNext()) {
                        JSONObject jsonObject1 = (JSONObject) iterator.next();
                        String targetId = jsonObject1.getString("targetId");
                        //1具体人员 2角色 3部门
                        if (AssigneeTypeEnum.USER.equals(jsonObject1.getString("type"))) {
                            assigneeList.add(targetId);
                        } else if (AssigneeTypeEnum.ROLE.equals(jsonObject1.getString("type"))) {
                            //按角色查询用户id
                            QueryWrapper<RuRelation> ruWrapper = new QueryWrapper<>();
                            ruWrapper.eq("ROLEID", targetId);
                            List<RuRelation> ruRelations = ruRelationMapper.selectList(ruWrapper);
                            if (!CollectionUtils.isEmpty(ruRelations)) {
                                List<String> collect = ruRelations.stream().map(RuRelation::getUserId).map(x -> x + "").collect(Collectors.toList());
                                assigneeList.addAll(collect);
                            }
                        } else if (AssigneeTypeEnum.DEPT.equals(jsonObject1.getString("type"))) {
                            //按部门查询用户id
                            List<Map<String, Object>> list = userMapper.selectUsers(null, null, null, targetId);
                            if (!CollectionUtils.isEmpty(list)) {
                                list.stream().forEach(m -> {
                                    String id = String.valueOf(m.get("id"));
                                    assigneeList.add(id);
                                });
                            }
                        }
                    }
                } else if (AssigneeSetTypeEnum.LEADER == settype) {
                    // 选择主管作为审批人
                    String startUserId = execution.getVariable("root", String.class);
                    User user = userMapper.selectById(startUserId);
                    //根据发起人查部门，根据部门查主管角色查主管，每个部门只有一个主管，根据角色和部门获取主管
                    //sys_role表tips字段区分角色，director定义为主管角色
                    List<Map<String, Object>> list = SqlRunner.db().selectList("select su.ID as id from sys_role sr " +
                            "JOIN sys_ru_relation srr ON sr.ID = srr.ROLEID " +
                            "JOIN sys_user su ON srr.USERID = su.ID " +
                            "WHERE sr.DEPT_ID = {0}" +
                            "AND sr.TIPS = {1}" +
                            "AND su.DEPT_ID = {0}", String.valueOf(user.getDeptId()), "director");
                    Map<String, Object> idMap = list.get(0);
                    String id = String.valueOf(idMap.get("id"));
                    assigneeList.add(id);
                } else if (AssigneeSetTypeEnum.SELF_SELECT == settype) {
                    // TODO: 2022/12/27 发起人自选审批人(需求暂无此功能)

                } else if (AssigneeSetTypeEnum.SELF == settype) {
                    //启动流程时设置的发起人全局变量
                    String startUserId = execution.getVariable("root", String.class);
                    assigneeList.add(startUserId);
                } else if (AssigneeSetTypeEnum.LEADER_TOP == settype) {
                    //连续多级主管作为审批人，审批方式由多人审批时审批方式字段决定
                    //根据发起人查部门，根据部门查主管角色，根据发起人部门查父部门，根据父部门查主管角色，递归

                    //多实例加签，解决手动加签的情况，这里是还未设置审批人，无需采用多实例加签的功能，增加到assigneeList即可
                    //runtimeService.addMultiInstanceExecution();

                    String startUserId = execution.getVariable("root", String.class);

                    //循环查询发起人的主管id
                    //directorLevel一定大于1，前端做下拉限制从2级起
                    Integer directorLevel = nodeConfig.getInteger("directorLevel");
                    for (int i = 1; i <= directorLevel; i++) {
                        User user = userMapper.selectById(startUserId);
                        Long deptId = user.getDeptId();
                        Dept dept = deptMapper.selectById(deptId);
                        String pids = dept.getPids();
                        List<String> deptIds = new ArrayList<>();
                        if(StringUtils.isNotEmpty(pids)){
                            String replace = pids.replace("[", "").replace("]", "");
                            pids = replace.substring(0, replace.length() - 1);
                            //在角色管理中配置的角色所在组织机构，要保证该机构的子组织结构使用该角色也可以生效
                            deptIds = new ArrayList<>(Arrays.asList(pids.split(",")));
                        }
                        if(i > 1){
                            //主管获取所在的上级部门
                            deptId = dept.getPid();
                        }
                        //保证无父id时deptIds有值，sql正常执行
                        if(CollectionUtils.isEmpty(deptIds)){
                            deptIds.add(String.valueOf(deptId));
                        }
                        List<Map<String, Object>> list = SqlRunner.db().selectList("select su.ID as id from sys_role sr " +
                                "JOIN sys_ru_relation srr ON sr.ID = srr.ROLEID " +
                                "JOIN sys_user su ON srr.USERID = su.ID " +
                                "WHERE (sr.DEPT_ID = {2} or sr.DEPT_ID in {0})" +
                                "AND sr.TIPS = {1}" +
                                "AND su.DEPT_ID = {2}", deptIds, "director", String.valueOf(deptId));
                        if(CollectionUtils.isEmpty(list)){
                            break;
                        }
                        Map<String, Object> idMap = list.get(0);
                        String id = String.valueOf(idMap.get("id"));
                        assigneeList.add(id);
                        startUserId = id;
                    }
                    // TODO: 2023/1/13 以上逻辑需联调测试验证
                }
            }

            //无审批人时，根据参数（noHanderAction）判断自动驳回or自动通过
            if (CollectionUtils.isEmpty(assigneeList)) {
                //1自动审批通过 2自动审批拒绝 3转交给审核管理员
                Integer noHanderAction = nodeUserJsonObject.getInteger("noHanderAction");
                if (1 == noHanderAction) {
                    assigneeList.add("100000");
                    execution.setVariable(variable, assigneeList);
                } else if (2 == noHanderAction) {
                    execution.setVariable("autoRefuse", Boolean.TRUE);
                    assigneeList.add("100000");
                    execution.setVariable(variable, assigneeList);
                } else if (3 == noHanderAction) {
                    assigneeList.add("0");
                    execution.setVariable(variable, assigneeList);
                } else {
                    log.error("监听器获取审批人失败，请检查流程审批人配置，flowDef json：{}", systemFlowForm.getFlowDef());
                }
            } else {
                execution.setVariable(variable, assigneeList);
            }
        }
    }

    /**
     * 递归获取匹配taskId的节点
     * @param nodeConfig
     * @param taskId
     * @return
     */
    private JSONObject getNodeUserList(JSONObject nodeConfig, String taskId) {
        JSONObject childNode = nodeConfig.getJSONObject("childNode");
        if (!Objects.isNull(childNode) && StringUtils.isNotEmpty(childNode.getString("id")) && taskId.equals(childNode.getString("id"))) {
            return childNode;
        }
        if (!Objects.isNull(childNode) && Objects.isNull(childNode.getJSONArray("conditionNodes"))) {
            return getNodeUserList(childNode, taskId);
        }else if(!Objects.isNull(childNode) && !Objects.isNull(childNode.getJSONArray("conditionNodes"))){
            //条件节点判断
            JSONArray conditionNodes = childNode.getJSONArray("conditionNodes");
            for (Object conditionNode : conditionNodes) {
                JSONObject node = (JSONObject)conditionNode;
                if(Objects.isNull(getNodeUserList(node, taskId))){
                    continue;
                }
                //条件节点一定能匹配到任务审批人
                return getNodeUserList(node, taskId);
            }
        }
        return null;
    }

}
