package cn.fateverse.workflow.service;

import cn.fateverse.admin.dubbo.DubboUserService;
import cn.fateverse.admin.entity.Dept;
import cn.fateverse.admin.entity.User;
import cn.fateverse.admin.vo.UserVo;
import cn.fateverse.common.core.exception.CustomException;
import cn.fateverse.workflow.constant.ProcessConstant;
import cn.fateverse.workflow.entity.bpmn.ProcessNode;
import cn.fateverse.workflow.entity.bpmn.Properties;
import cn.fateverse.workflow.entity.bpmn.RoleInfo;
import cn.fateverse.workflow.entity.bpmn.UserInfo;
import cn.fateverse.workflow.entity.vo.HistoricalOperationVo;
import cn.fateverse.workflow.enums.OperationStateEnums;
import cn.fateverse.workflow.enums.AssigneeTypeEnums;
import cn.fateverse.workflow.enums.ProcessNodeEnum;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * 流程节点操作服务
 *
 * @author Clay
 * @date 2023-02-26
 */
@Component
public class ProcessNodeService {

    private final ThreadPoolExecutor executor;

    @DubboReference
    private DubboUserService userService;


    public ProcessNodeService() {
        executor = new ThreadPoolExecutor(2,
                4,
                60,
                TimeUnit.SECONDS, new LinkedBlockingDeque<>(128),
                new ThreadFactoryBuilder().setNameFormat("process_%d").build(),
                new ThreadPoolExecutor.CallerRunsPolicy());
    }


    /**
     * 获取到流程节点数据信息
     *
     * @param processStr 流程数据字符串
     * @return 流程节点数据
     */
    public List<ProcessNode> getProcessNodes(String processStr, User user, List<HistoricalOperationVo> operationList,
                                             JSONObject optionalUser, Boolean isStart) {
        Date startTime = new Date();
        Map<String, List<UserInfo>> operationInfoMap = operationList.stream().filter(operation -> null != operation.getUserInfo()).collect(Collectors.toMap(HistoricalOperationVo::getNodeId, HistoricalOperationVo::getUserInfo));
        ProcessNodeWrapper wrapper = new ProcessNodeWrapper(operationInfoMap, optionalUser, user, isStart);
        //todo 获取到用户信息.将root节点的信息进更改,后期为对所有的节点数据进行处理
        List<ProcessNode> processNodeList = JSON.parseArray(processStr, ProcessNode.class);
        CountDownLatch countDownLatch = new CountDownLatch(processNodeList.size());
        for (ProcessNode processNode : processNodeList) {
            executor.submit(() -> {
                try {
                    doInitProcessNode(processNode, wrapper);
                } finally {
                    countDownLatch.countDown();
                }
            });
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        wrapper.clearCache();
        System.out.println("用时 :" + (new Date().getTime() - startTime.getTime()));
        return wrapper.result;
    }


    /**
     * 根据角色查询
     *
     * @param roleIds 角色id列表
     * @return 用户信息
     */
    private List<UserInfo> searchUserListByRoleIds(List<Long> roleIds, ProcessNodeWrapper wrapper) {
        List<UserVo> result = new ArrayList<>();
        List<Long> roleIdsNew = new ArrayList<>();
        roleIds.forEach(roleId -> {
            if (wrapper.cacheRoleCacheUserMap.containsKey(roleId)) {
                result.addAll(wrapper.cacheRoleCacheUserMap.get(roleId));
            } else {
                roleIdsNew.add(roleId);
            }
        });
        if (!roleIdsNew.isEmpty()) {
            List<UserVo> userList = userService.searchUserListByRoleIds(roleIdsNew);
            Map<Long, List<UserVo>> userRole = userList.stream().collect(Collectors.groupingBy(UserVo::getRoleId));
            wrapper.cacheRoleCacheUserMap.putAll(userRole);
            result.addAll(userList);
        }
        return result.stream().map(userVo ->
                UserInfo.toUserInfo(userVo, OperationStateEnums.UNACTIVATED)).collect(Collectors.toList());
    }

    /**
     * 根据部门查询数据信息
     *
     * @param deptIds 部门id列表
     * @return 用户信息
     */
    private List<UserInfo> searchUserByDeptIds(List<Long> deptIds, ProcessNodeWrapper wrapper) {
        List<UserInfo> result = new ArrayList<>();
        List<Long> deptIdsNew = new ArrayList<>();
        deptIds.forEach(deptId -> {
            if (wrapper.cacheDeptCacheUserMap.containsKey(deptId)) {
                result.add(wrapper.cacheDeptCacheUserMap.get(deptId));
            } else {
                deptIdsNew.add(deptId);
            }
        });
        if (!deptIdsNew.isEmpty()) {
            List<UserVo> userList = userService.searchUserByDeptIds(deptIdsNew);
            userList.forEach(userVo -> {
                UserInfo value = UserInfo.toUserInfo(userVo, OperationStateEnums.UNACTIVATED);
                result.add(value);
                wrapper.cacheDeptCacheUserMap.put(userVo.getLeaderDeptId(), value);
            });
        }
        return result;
    }

    /**
     * rpc查询用户数据
     *
     * @param userIds
     * @return 用户信息列表
     */
    private List<UserInfo> searchUserListByUserIds(List<Long> userIds, ProcessNodeWrapper wrapper) {
        List<UserInfo> result = new ArrayList<>();
        List<Long> userIdsNew = new ArrayList<>();
        userIds.forEach(userId -> {
            if (wrapper.cacheUserMap.containsKey(userId)) {
                result.add(wrapper.cacheUserMap.get(userId));
            } else {
                userIdsNew.add(userId);
            }
        });
        if (!userIdsNew.isEmpty()) {
            List<UserVo> userList = userService.searchUserListByUserIds(userIdsNew);
            userList.forEach(userVo -> {
                UserInfo value = UserInfo.toUserInfo(userVo, OperationStateEnums.UNACTIVATED);
                result.add(value);
                wrapper.cacheUserMap.put(userVo.getUserId(), value);
            });
        }
        return result;
    }

    /**
     * 设置返回集合
     *
     * @param processNode
     */
    private void setResult(ProcessNode processNode, ProcessNodeWrapper wrapper) {
        synchronized (wrapper.result) {
            wrapper.result.add(processNode);
        }
    }

    /**
     * 对流程节点进筛选处理
     *
     * @param processNode
     */
    private void doInitProcessNode(ProcessNode processNode, ProcessNodeWrapper wrapper) {
        if (processNode.getId().equals(ProcessConstant.ROOT_NODE)) {
            processNodeSelf(processNode, wrapper);
            return;
        }
        List<UserInfo> operationInfoList = wrapper.userOperationInfoMap.get(processNode.getId());
        //为空则表示未被激活,激活后的都会在有一条操作记录
        Properties props = processNode.getProps();
        if (null == operationInfoList || operationInfoList.isEmpty()) {
            if (ProcessNodeEnum.APPROVAL.equals(processNode.getType())) {
                //未获取到用户id则可能是分支节点,获取是未激活的节点(未激活的节点则会有 抄送节点(已选择好人 或者)和审核节点)
                AssigneeTypeEnums assignedType = props.getAssignedType();
                if (!wrapper.isStart) {
                    props.setAssignedType(AssigneeTypeEnums.ASSIGN_USER);
                }
                switch (assignedType) {
                    case SELF:
                        processNodeSelf(processNode, wrapper);
                        break;
                    case SELF_SELECT:
                        processNodeSelfSelect(processNode, wrapper);
                        break;
                    case ROLE:
                        processNodeRole(processNode, wrapper);
                        break;
                    case LEADER:
                        processNodeLeader(processNode, wrapper);
                        break;
                    case LEADER_TOP:
                        processNodeLeaderTop(processNode, wrapper);
                        break;
                    case ASSIGN_USER:
                    default:
                        setResult(processNode, wrapper);
                        break;
                }
            } else if (ProcessNodeEnum.CC.equals(processNode.getType())) {
                processNodeCC(processNode, wrapper);
            } else {
                setResult(processNode, wrapper);
            }
        } else {
            props.setAssignedUser(operationInfoList);
            if (!wrapper.isStart) {
                props.setAssignedType(AssigneeTypeEnums.ASSIGN_USER);
            }
            setResult(processNode, wrapper);
        }
    }

    private void processNodeCC(ProcessNode processNode, ProcessNodeWrapper wrapper) {
        List<UserInfo> userList = wrapper.optionalUser.getList(processNode.getId(), UserInfo.class);
        if (null != userList && !userList.isEmpty()) {
            Properties props = processNode.getProps();
            props.setAssignedUser(userList);
            if (!wrapper.isStart) {
                props.setAssignedType(AssigneeTypeEnums.ASSIGN_USER);
            } else {
                processNode.getProps().setAssignedType(null);
            }
        }
        setResult(processNode, wrapper);
    }

    /**
     * 用户自选
     *
     * @param processNode
     */
    private void processNodeSelfSelect(ProcessNode processNode, ProcessNodeWrapper wrapper) {
        List<UserInfo> userList = wrapper.optionalUser.getList(processNode.getId(), UserInfo.class);
        if (null != userList && !userList.isEmpty()) {
            Properties props = processNode.getProps();
            props.setAssignedUser(userList);
            if (!wrapper.isStart) {
                processNode.getProps().setAssignedType(AssigneeTypeEnums.ASSIGN_USER);
            } else {
                processNode.getProps().setAssignedType(AssigneeTypeEnums.SELF_SELECT);
            }
        }
        setResult(processNode, wrapper);
    }

    private void processNodeLeaderTop(ProcessNode processNode, ProcessNodeWrapper wrapper) {
        Properties props = processNode.getProps();
        List<UserInfo> assignedUser;
        Map<String, Object> leaderTop = props.getLeaderTop();
        String endCondition = leaderTop.get("endCondition").toString();
        Dept dept = wrapper.user.getDept();
        List<Long> parentIds = Arrays.stream(dept.getAncestors().split(",")).map(Long::valueOf).collect(Collectors.toList());
        switch (endCondition) {
            case "TOP"://直接到最上层
                assignedUser = searchUserByDeptIds(parentIds.subList(1, parentIds.size()), wrapper);
                break;
            case "LEAVE"://到指定级别
                int level = Integer.parseInt(leaderTop.get("level").toString());
                List<Long> leaderDeptIds = null;
                int length = parentIds.size();
                if (level > length) {
                    leaderDeptIds = parentIds.subList(1, parentIds.size());
                } else {
                    leaderDeptIds = parentIds.subList(parentIds.size() - level, parentIds.size());
                }
                assignedUser = searchUserByDeptIds(leaderDeptIds, wrapper);
                break;
            default:
                throw new CustomException("当前选项还未实现!");
        }
        processNode.getProps().setAssignedUser(assignedUser);
        processNode.getProps().setAssignedType(AssigneeTypeEnums.ASSIGN_USER);
        setResult(processNode, wrapper);
    }

    private void processNodeLeader(ProcessNode processNode, ProcessNodeWrapper wrapper) {
        List<UserInfo> assignedUser;
        Properties props = processNode.getProps();
        int level = Integer.parseInt(props.getLeader().get("level").toString());
        Dept dept = wrapper.user.getDept();
        if (1 == level) {
            assignedUser = searchUserListByUserIds(Collections.singletonList(dept.getDeptId()), wrapper);
        } else {
            List<Long> parentIds = Arrays.stream(dept.getAncestors().split(",")).map(Long::valueOf).collect(Collectors.toList());
            Long leaderDeptId = null;
            int length = parentIds.size() - 1;
            if (level > length) {
                leaderDeptId = parentIds.get(1);
            } else {
                leaderDeptId = parentIds.get(parentIds.size() - level);
            }
            assignedUser = searchUserByDeptIds(Collections.singletonList(leaderDeptId), wrapper);
        }
        props.setAssignedUser(assignedUser);
        props.setAssignedType(AssigneeTypeEnums.ASSIGN_USER);
        setResult(processNode, wrapper);
    }

    /**
     * 流程节点的用户为自己
     *
     * @param processNode 流程节点
     */
    private void processNodeSelf(ProcessNode processNode, ProcessNodeWrapper wrapper) {
        Properties props = processNode.getProps();
        List<UserInfo> assignedUser;
        assignedUser = new ArrayList<>();
        UserInfo userInfo = UserInfo.toUserInfo(wrapper.user, OperationStateEnums.UNACTIVATED);
        assignedUser.add(userInfo);
        props.setAssignedUser(assignedUser);
        props.setAssignedType(AssigneeTypeEnums.ASSIGN_USER);
        setResult(processNode, wrapper);
    }

    /**
     * 流程节点选择的为角色
     *
     * @param processNode 流程节点
     */
    private void processNodeRole(ProcessNode processNode, ProcessNodeWrapper wrapper) {
        Properties props = processNode.getProps();
        List<UserInfo> assignedUser;
        List<Long> roleIds = props.getRoleList().stream().map(RoleInfo::getRoleId).collect(Collectors.toList());
        //rpc远程通过角色id获取到当前角色下的所有用户数据
        assignedUser = searchUserListByRoleIds(roleIds, wrapper);
        props.setAssignedUser(assignedUser);
        props.setAssignedType(AssigneeTypeEnums.ASSIGN_USER);
        setResult(processNode, wrapper);
    }


    private static class ProcessNodeWrapper {


        //用户id缓存
        private final Map<String, List<Long>> cacheUserIdMap;
        //角色用户缓存
        private final Map<Long, List<UserVo>> cacheRoleCacheUserMap;
        //部门用户缓存
        private final Map<Long, UserInfo> cacheDeptCacheUserMap;
        //用户缓存信息
        private final Map<Long, UserInfo> cacheUserMap;

        //操作历史缓存
        private final Map<String, List<UserInfo>> userOperationInfoMap;
        //自选用户信息
        private final JSONObject optionalUser;
        private final List<ProcessNode> result = new ArrayList<>();
        private final User user;
        private final Boolean isStart;


        public ProcessNodeWrapper(Map<String, List<UserInfo>> userOperationInfoMap, JSONObject optionalUser, User user, Boolean isStart) {
            this.userOperationInfoMap = userOperationInfoMap;
            this.optionalUser = optionalUser;
            this.user = user;
            this.isStart = isStart;
            this.cacheUserIdMap = new ConcurrentHashMap<>();
            this.cacheRoleCacheUserMap = new ConcurrentHashMap<>();
            this.cacheDeptCacheUserMap = new ConcurrentHashMap<>();
            this.cacheUserMap = new ConcurrentHashMap<>();
        }

        /**
         * 清除缓存,当空间大于16时进行清除
         */
        private void clearCache() {
            if (cacheRoleCacheUserMap.size() > 16) {
                cacheRoleCacheUserMap.clear();
            }
            if (cacheUserIdMap.size() > 16) {
                cacheUserIdMap.clear();
            }
            if (cacheUserMap.size() > 16) {
                cacheUserMap.clear();
            }
        }

    }
}
