/* (C) 2022 YiRing, Inc. */
package com.yiring.auth.util;

import com.yiring.auth.domain.permission.Permission;
import com.yiring.auth.domain.role.Role;
import com.yiring.auth.vo.permission.MenuVo;
import com.yiring.auth.vo.permission.PermissionVo;
import com.yiring.auth.vo.role.RoleVo;
import com.yiring.common.util.Commons;
import jakarta.annotation.Nullable;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.NonNull;
import lombok.experimental.UtilityClass;
import org.springframework.beans.BeanUtils;

/**
 * 权限工具栏
 *
 * @author Jim
 * @version 0.1
 * 2022/3/24 17:46
 */

@UtilityClass
public class Permissions {

    /**
     * 将角色集合转换成 Vo 集合
     *
     * @param roles 角色集合
     * @return vos
     */
    public List<RoleVo> toRoleVos(Set<Role> roles) {
        return roles
            .stream()
            .map(role -> Commons.transform(role, RoleVo.class, Role.Fields.permissions))
            .collect(Collectors.toList());
    }

    /**
     * 将权限集合转换成菜单树
     *
     * @param permissions 权限集合
     * @return 菜单树
     */
    public static List<MenuVo> toMenuTreeVo(@Nullable List<Permission> permissions) {
        List<MenuVo> list = Commons.transform(
            permissions,
            MenuVo.class,
            (source, target) -> target.setMeta(source.getMetaJson()),
            Permission.Fields.meta
        );

        // 返回的树
        ArrayList<MenuVo> roots = new ArrayList<>();

        // 将数据添加到 Map
        Map<String, MenuVo> map = list.stream().collect(Collectors.toMap(MenuVo::getId, Function.identity()));

        list.forEach(entity -> {
            MenuVo menu = map.get(entity.getPid());
            if (null == menu) {
                roots.add(map.get(entity.getId()));
            } else {
                if (menu.getChildren() == null) {
                    menu.setChildren(new ArrayList<>());
                }

                menu.getChildren().add(entity);
            }
        });

        return new ArrayList<>(sortMenuTreeVo(roots));
    }

    /**
     * 菜单树递归排序
     *
     * @param menus 菜单集合
     * @return 排序后的菜单集合
     */
    public static List<MenuVo> sortMenuTreeVo(List<MenuVo> menus) {
        return menus
            .stream()
            .sorted(
                Comparator.comparing(
                    item -> item.getMeta().getIntValue("orderNo"),
                    Comparator.nullsFirst(Comparator.naturalOrder())
                )
            )
            .peek(item -> {
                if (Commons.isNotEmpty(item.getChildren())) {
                    item.setChildren(sortMenuTreeVo(item.getChildren()));
                }
            })
            .toList();
    }

    /**
     * 将权限集合转换成 Vo 集合
     *
     * @param permissions 权限集合
     * @return vos
     */
    public List<PermissionVo> toPermissionVos(List<Permission> permissions) {
        return permissions
            .stream()
            .map(permission -> {
                PermissionVo vo = new PermissionVo();
                BeanUtils.copyProperties(permission, vo);
                vo.setMeta(permission.getMetaJson());
                return vo;
            })
            .collect(Collectors.toList());
    }

    /**
     * 提取角色集合含有的权限去重结果
     *
     * @param roles 角色集合
     * @return 权限集合
     */
    public List<Permission> toPermissions(Set<Role> roles) {
        // 提取角色中的所有权限并去重, 同时按照树节点标识排序
        return roles
            .stream()
            .map(Role::getPermissions)
            .flatMap(Set::stream)
            .filter(permission -> Boolean.FALSE.equals(permission.getDeleted()))
            .distinct()
            .sorted(Comparator.comparing(Permission::getTree, Comparator.comparingInt(String::length)))
            .collect(Collectors.toList());
    }

    /**
     * 根据 pid 构建树状权限集合
     *
     * @param permissions 权限集合
     * @param pid         权限父级 ID
     * @return 树状权限集合
     */
    public List<PermissionVo> toTree(List<Permission> permissions, @NonNull String pid) {
        return permissions
            .stream()
            .filter(permission -> pid.equals(permission.getPid()))
            .map(permission -> {
                PermissionVo vo = new PermissionVo();
                BeanUtils.copyProperties(permission, vo);
                vo.setMeta(permission.getMetaJson());
                vo.setChildren(toTree(permissions, permission.getId()));
                return vo;
            })
            .collect(Collectors.toList());
    }
}
