package cn.ey.biz.module.system.api.auth;

import cn.ey.biz.common.enums.CommonStatusEnum;
import cn.ey.biz.common.pojo.R;
import cn.ey.biz.common.util.collection.SetUtils;
import cn.ey.biz.module.system.api.auth.dto.AuthCodeBlockPermissionDTO;
import cn.ey.biz.module.system.api.auth.dto.AuthMenuDTO;
import cn.ey.biz.module.system.api.auth.dto.AuthMenuDTOAndCodeBlock;
import cn.ey.biz.module.system.api.auth.dto.AuthPermissionInfoDTO;
import cn.ey.biz.module.system.convert.auth.AuthConvert;
import cn.ey.biz.module.system.dal.dataobject.permission.MenuDO;
import cn.ey.biz.module.system.dal.dataobject.permission.RoleDO;
import cn.ey.biz.module.system.dal.dataobject.user.AdminUserDO;
import cn.ey.biz.module.system.enums.permission.MenuTypeEnum;
import cn.ey.biz.module.system.service.abac.AbacService;
import cn.ey.biz.module.system.service.i18n.I18nService;
import cn.ey.biz.module.system.service.permission.PermissionService;
import cn.ey.biz.module.system.service.permission.RoleService;
import cn.ey.biz.module.system.service.user.AdminUserService;
import cn.hutool.core.collection.CollectionUtil;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
import static cn.ey.biz.common.pojo.R.success;
import static cn.ey.biz.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.ey.biz.module.system.enums.ApiConstants.VERSION;
import static java.util.Collections.singleton;

@RestController // 提供 RESTful API 接口，给 Feign 调用
@DubboService(version = VERSION) // 提供 Dubbo RPC 接口，给 Dubbo Consumer 调用
@Validated
public class AuthApiImpl implements AuthApi {

    @Resource
    private PermissionService permissionService;
    @Resource
    private I18nService i18nService;
    @Resource
    private AdminUserService userService;
    @Resource
    private RoleService roleService;
    @Resource
    private AbacService abacService;

    @Override
    public R<List<AuthMenuDTO>> getMenus(String app, String acceptLanguage) {
        // 获得用户信息
        AdminUserDO user = userService.getUser(getLoginUserId());
        if (user == null) {
            return null;
        }

        // 获得角色列表
        Set<Long> roleIds = permissionService.getUserRoleIdsFromCache(user.getId(), singleton(CommonStatusEnum.ENABLE.getStatus()));

        // ABAC
        Set<Long> menuIds = new HashSet<>();

        // 第一次调用 获取的新角色，作为第二次调用的 判断条件
        abacService.checkAbac(user, roleIds, menuIds, null, app);
        abacService.checkAbac(user, roleIds, menuIds, null, app);

        // 获得用户拥有的菜单列表
        List<MenuDO> menuList = permissionService.getRoleMenuListFromCache(roleIds,
                SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType()), // 只要目录和菜单类型
                singleton(CommonStatusEnum.ENABLE.getStatus()), menuIds); // 只要开启的
        // 多语言转化
        menuList.forEach(item -> {
            item.setName(i18nService.getLabel(String.valueOf(item.getId()),
                    acceptLanguage,
                    "menu"));
        });
        // 转换成 Tree 结构返回
        List<MenuDO> appList = menuList.stream().filter(d -> app.equals(d.getApp())).collect(Collectors.toList());
        return success(AuthConvert.INSTANCE.buildMenuTreeApi(appList));
    }

    @Override
    public R<AuthPermissionInfoDTO> getPermissionInfo(String app) {
        // 获得用户信息
        AdminUserDO user = userService.getUser(getLoginUserId());
        if (user == null) {
            return null;
        }

        // 获得角色列表
        Set<Long> roleIds = permissionService.getUserRoleIdsFromCache(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus()));

        // ABAC
        Set<Long> menuIds = new HashSet<>();
        if (app != null) {
            // 第一次调用 获取的新角色，作为第二次调用的 判断条件
            abacService.checkAbac(user, roleIds, menuIds, null, app);
            abacService.checkAbac(user, roleIds, menuIds, null, app);
        }

        List<RoleDO> roleList = roleService.getRolesFromCache(roleIds);

        // 获得菜单列表
        List<MenuDO> menuList = permissionService.getRoleMenuListFromCache(roleIds,
                SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType(), MenuTypeEnum.BUTTON.getType()),
                singleton(CommonStatusEnum.ENABLE.getStatus()), menuIds); // 只要开启的
        // 拼接结果返回
        return success(AuthConvert.INSTANCE.convertApi(user, roleList, menuList));
    }

    @Override
    public R<List<AuthMenuDTO>> getMenusByCodeBlock(String app, String scene, String codeblock, String acceptLanguage, Map<String, Object> dynamicAttr) {
        AuthCodeBlockPermissionDTO permission = abacService.checkAbac(codeblock, dynamicAttr, app, scene);

        // 获得用户拥有的菜单列表
        List<MenuDO> menuList = permissionService.getRoleMenuListFromCache(permission.getRoleIds(),
                SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType()), // 只要目录和菜单类型
                singleton(CommonStatusEnum.ENABLE.getStatus()), permission.getMenuIds()); // 只要开启的
        // 多语言转化
        menuList.forEach(item -> {
            item.setName(i18nService.getLabel(String.valueOf(item.getId()),
                    acceptLanguage,
                    "menu"));
        });
        // 转换成 Tree 结构返回
        List<MenuDO> appList = menuList.stream().filter(d -> app.equals(d.getApp())).collect(Collectors.toList());
        return success(AuthConvert.INSTANCE.buildMenuTreeApi(appList));
    }


    @Override
    public R<AuthPermissionInfoDTO> getPermissionInfoByCodeBlock(String app, String scene, String codeblock, Map<String, Object> dynamicAttr) {
        AuthCodeBlockPermissionDTO permission = abacService.checkAbac(codeblock, dynamicAttr, app, scene);

        List<RoleDO> roleList = roleService.getRolesFromCache(permission.getRoleIds());

        List<MenuDO> menuList = permissionService.getRoleMenuListFromCache(permission.getRoleIds(),
                SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType(), MenuTypeEnum.BUTTON.getType()),
                singleton(CommonStatusEnum.ENABLE.getStatus()), permission.getMenuIds()); // 只要开启的
        // 拼接结果返回
        return success(AuthConvert.INSTANCE.convertApi(roleList, menuList));
    }


    @Override
    public R<List<AuthMenuDTOAndCodeBlock>> getMenusByCodeBlockList(String app, String scene, Collection<String> codeblocks, String acceptLanguage, Map<String, Object> dynamicAttr) {
        List<AuthMenuDTOAndCodeBlock> authMenuDTOAndCodeBlockList=new ArrayList<AuthMenuDTOAndCodeBlock>();

        List<AuthCodeBlockPermissionDTO> permissionList = abacService.checkAbacList(codeblocks, dynamicAttr, app, scene);
        if(CollectionUtil.isNotEmpty(permissionList)){
            for(AuthCodeBlockPermissionDTO permission:permissionList){
                // 获得用户拥有的菜单列表
                List<MenuDO> menuList = permissionService.getRoleMenuListFromCache(permission.getRoleIds(),
                        SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType()), // 只要目录和菜单类型
                        singleton(CommonStatusEnum.ENABLE.getStatus()), permission.getMenuIds()); // 只要开启的
                // 多语言转化
                menuList.forEach(item -> {
                    item.setName(i18nService.getLabel(String.valueOf(item.getId()),
                            acceptLanguage,
                            "menu"));
                });
                // 转换成 Tree 结构返回
                List<MenuDO> appList = menuList.stream().filter(d -> app.equals(d.getApp())).collect(Collectors.toList());
                List<AuthMenuDTO> authMenuDTOList=AuthConvert.INSTANCE.buildMenuTreeApi(appList);

                AuthMenuDTOAndCodeBlock authMenuDTOAndCodeBlock=new AuthMenuDTOAndCodeBlock();
                authMenuDTOAndCodeBlock.setAuthMenuDTOList(authMenuDTOList);
                authMenuDTOAndCodeBlock.setCodeBlock(permission.getCodeBlock());
                authMenuDTOAndCodeBlockList.add(authMenuDTOAndCodeBlock);
            }
        }
        return success(authMenuDTOAndCodeBlockList);
    }


}
