package com.xjrsoft.module.system.service.impl;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xjrsoft.common.constant.GlobalConstant;
import com.xjrsoft.common.enums.AuthorizeType;
import com.xjrsoft.common.enums.EnabledMark;
import com.xjrsoft.common.enums.MenuType;
import com.xjrsoft.common.model.tree.ITreeNode;
import com.xjrsoft.common.utils.TreeUtil;
import com.xjrsoft.common.utils.VoToColumnUtil;
import com.xjrsoft.module.magicapi.entity.InterfaceAuth;
import com.xjrsoft.module.magicapi.mapper.InterfaceAuthMapper;
import com.xjrsoft.module.organization.dto.SetRoleAuthDto;
import com.xjrsoft.module.organization.entity.Department;
import com.xjrsoft.module.organization.entity.Post;
import com.xjrsoft.module.organization.entity.Role;
import com.xjrsoft.module.organization.entity.UserRoleRelation;
import com.xjrsoft.module.organization.mapper.RoleMapper;
import com.xjrsoft.module.organization.mapper.UserDeptRelationMapper;
import com.xjrsoft.module.organization.mapper.UserPostRelationMapper;
import com.xjrsoft.module.organization.mapper.UserRoleRelationMapper;
import com.xjrsoft.module.organization.vo.RoleAuthVo;
import com.xjrsoft.module.organization.vo.UserRoleVo;
import com.xjrsoft.module.system.entity.Authorize;
import com.xjrsoft.module.system.entity.Menu;
import com.xjrsoft.module.system.entity.MenuButton;
import com.xjrsoft.module.system.entity.MenuColumn;
import com.xjrsoft.module.system.entity.MenuForm;
import com.xjrsoft.module.system.mapper.AuthorizeMapper;
import com.xjrsoft.module.system.mapper.MenuButtonMapper;
import com.xjrsoft.module.system.mapper.MenuColumnMapper;
import com.xjrsoft.module.system.mapper.MenuFormMapper;
import com.xjrsoft.module.system.mapper.MenuMapper;
import com.xjrsoft.module.system.service.IAuthorizeService;
import com.xjrsoft.module.system.vo.MenuAuthVo;
import com.xjrsoft.module.system.vo.MenuFormTreeVo;
import com.xjrsoft.module.system.vo.MenuTreeVo;
import com.xjrsoft.module.system.vo.PermissionVo;
import lombok.AllArgsConstructor;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author tzx
 * @since 2022-03-16
 */
@Service
@AllArgsConstructor
public class AuthorizeServiceImpl extends ServiceImpl<AuthorizeMapper, Authorize> implements IAuthorizeService {

    private final UserRoleRelationMapper userRoleRelationMapper;

    private final UserDeptRelationMapper userDeptRelationMapper;

    private final UserPostRelationMapper userPostRelationMapper;

    private final RoleMapper roleMapper;

    private final AuthorizeMapper authorizeMapper;

    private final InterfaceAuthMapper interfaceAuthMapper;

    private final MenuButtonMapper menuButtonMapper;

    private final MenuColumnMapper menuColumnMapper;

    private final MenuFormMapper menuFormMapper;

    private final MenuMapper menuMapper;


    @Override
    public PermissionVo getPermissions() {

        PermissionVo result = new PermissionVo();
        List<MenuAuthVo> authVoList = new ArrayList<>();
        result.setMenuAuthList(authVoList);

        long userId = StpUtil.getLoginIdAsLong();
        List<UserRoleRelation> relations = userRoleRelationMapper.selectList(Wrappers.lambdaQuery(UserRoleRelation.class)
                .eq(UserRoleRelation::getUserId, userId));

        List<Long> relationIds = relations.stream().map(UserRoleRelation::getRoleId).collect(Collectors.toList());

        List<Role> roleList = roleMapper.selectList(Wrappers.lambdaQuery(Role.class)
                .select(Role.class, x -> VoToColumnUtil.fieldsToColumns(UserRoleVo.class).contains(x.getColumn()))
                .in(relationIds.size() > 0, Role::getId, relationIds));

        List<String> roleCodeList = roleList.stream().map(Role::getCode).collect(Collectors.toList());
        List<Long> roleIdList = roleList.stream().map(Role::getId).collect(Collectors.toList());

        SaSession tokenSession = StpUtil.getTokenSession();
        Post post = tokenSession.get(GlobalConstant.LOGIN_USER_POST_INFO_KEY, new Post());
        Department department = tokenSession.get(GlobalConstant.LOGIN_USER_DEPT_INFO_KEY, new Department());


        result.setPostId(post.getId());
        result.setPostName(post.getName());
        result.setDepartmentId(department.getId());
        result.setDepartmentName(department.getName());

        //如果包含超级管理员权限  默认返回所有权限
        List<MenuButton> buttonList = new ArrayList<>();
        List<MenuColumn> columnList = new ArrayList<>();
        List<MenuForm> formList = new ArrayList<>();
        if (roleIdList.contains(GlobalConstant.SUPER_ADMIN_ROLE_ID)) {
            buttonList = menuButtonMapper.selectList(null);
            columnList = menuColumnMapper.selectList(null);
            formList = menuFormMapper.selectList(null);
        } else {
            List<Authorize> authorizeList = authorizeMapper.selectList(Wrappers.lambdaQuery(Authorize.class)
                    .in(roleIdList.size() > 0, Authorize::getRoleId, roleIdList)
            );

            List<Long> buttonIds = authorizeList.stream().filter(x -> x.getAuthorizeType() == AuthorizeType.BUTTON.getCode()).map(Authorize::getObjectId).collect(Collectors.toList());
            if (buttonIds.size() > 0) {
                LambdaQueryWrapper<MenuButton> queryWrapper = Wrappers.lambdaQuery(MenuButton.class);
                queryWrapper.in(MenuButton::getId, buttonIds).select(MenuButton::getMenuId, MenuButton::getCode);
                buttonList = menuButtonMapper.selectList(queryWrapper);
            }

            List<Long> columnIds = authorizeList.stream().filter(x -> x.getAuthorizeType() == AuthorizeType.COLUMN.getCode()).map(Authorize::getObjectId).collect(Collectors.toList());
            if (columnIds.size() > 0) {
                LambdaQueryWrapper<MenuColumn> queryWrapper = Wrappers.lambdaQuery(MenuColumn.class);
                queryWrapper.in(MenuColumn::getId, columnIds).select(MenuColumn::getMenuId, MenuColumn::getCode);
                columnList = menuColumnMapper.selectList(queryWrapper);
            }

            List<Long> formIds = authorizeList.stream().filter(x -> x.getAuthorizeType() == AuthorizeType.FORM.getCode()).map(Authorize::getObjectId).collect(Collectors.toList());
            if (formIds.size() > 0) {
                LambdaQueryWrapper<MenuForm> queryWrapper = Wrappers.lambdaQuery(MenuForm.class);
                queryWrapper.in(MenuForm::getId, formIds);
                formList = menuFormMapper.selectList(queryWrapper);
            }
        }

        List<Menu> menus = menuMapper.selectList(Wrappers.lambdaQuery(Menu.class).eq(Menu::getMenuType, MenuType.FUNCTION.getCode()).select(Menu::getId));
        List<MenuFormTreeVo> menuFormTreeVos = BeanUtil.copyToList(formList, MenuFormTreeVo.class);
        List<MenuFormTreeVo> formTreeVoList = TreeUtil.buildMapTree(menuFormTreeVos,0L);

        for (Menu menu : menus) {
            MenuAuthVo vo = new MenuAuthVo();
            vo.setMenuId(menu.getId());
            vo.setButtonAuthCode(buttonList.stream().filter(x -> x.getMenuId().equals(menu.getId())).map(MenuButton::getCode).collect(Collectors.toSet()));
            vo.setColumnAuthCode(columnList.stream().filter(x -> x.getMenuId().equals(menu.getId())).map(MenuColumn::getCode).collect(Collectors.toSet()));
            // 处理表单字段，可能存在子表的情况
            if (formTreeVoList != null){
                Set<Object> formAuthCodeList = new LinkedHashSet<>(formTreeVoList.size());
                for (MenuFormTreeVo treeVo : formTreeVoList) {
                    if (treeVo.getMenuId().equals(menu.getId())) {
                        List<MenuFormTreeVo> children = treeVo.getChildren();
                        if (CollectionUtils.isNotEmpty(children)) {
                            Set<String> subCodeList = new LinkedHashSet<>(children.size());
                            for (MenuFormTreeVo child : children) {
                                subCodeList.add(child.getCode());
                            }
                            Map<String, Set<String>> subTableCodeMap = new HashMap<>(1);
                            subTableCodeMap.put(treeVo.getCode(), subCodeList);
                            formAuthCodeList.add(subTableCodeMap);
                        } else {
                            formAuthCodeList.add(treeVo.getCode());
                        }
                    }
                }
                vo.setFormAuthCode(formAuthCodeList);
            }
            authVoList.add(vo);
        }

        // 自定义接口权限
        List<InterfaceAuth> interfaceAuthList = new ArrayList<>();
        if (roleIdList.size() > 0) { // 学生和家长 没有
            interfaceAuthList = interfaceAuthMapper.selectList(Wrappers.lambdaQuery(InterfaceAuth.class)
                    .in(InterfaceAuth::getRoleId, roleIdList));
        }
//        List<InterfaceAuth> interfaceAuthList = interfaceAuthMapper.selectList(Wrappers.lambdaQuery(InterfaceAuth.class)
//                .in(InterfaceAuth::getRoleId, roleIdList));
        if (CollectionUtils.isNotEmpty(interfaceAuthList)) {
            List<String> interfaceIdList = interfaceAuthList.stream().map(InterfaceAuth::getInterfaceId).collect(Collectors.toList());
            tokenSession.set(GlobalConstant.LOGIN_USER_INTERFACE_AUTH_CODE_KEY, interfaceIdList);
        }

        List<String> buttonCodeList = buttonList.stream().map(MenuButton::getCode).collect(Collectors.toList());

        tokenSession.set(GlobalConstant.LOGIN_USER_AUTH_CODE_KEY, buttonCodeList);
        tokenSession.set(GlobalConstant.LOGIN_USER_ROLE_CODE_KEY, roleCodeList);
        tokenSession.set(GlobalConstant.LOGIN_USER_ROLE_ID_KEY, roleIdList);

        return result;

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean setRoleAuth(SetRoleAuthDto dto) {

        //添加菜单权限
        Long id = dto.getId();
        List<Authorize> savedAuthList = new ArrayList<>();
        //菜单
        buildAuthorize(id, dto.getMenuIds(), AuthorizeType.MENU.getCode(), savedAuthList);
        //按钮
        buildAuthorize(id, dto.getButtonIds(), AuthorizeType.BUTTON.getCode(), savedAuthList);
        //列表字段
        buildAuthorize(id, dto.getColumnIds(), AuthorizeType.COLUMN.getCode(), savedAuthList);
        //表单字段
        buildAuthorize(id, dto.getFormIds(), AuthorizeType.FORM.getCode(), savedAuthList);

        //删除当前角色所有权限
        this.remove(Wrappers.<Authorize>query().lambda().eq(Authorize::getRoleId, id).in(Authorize::getAuthorizeType, 0, 1, 2, 3));
        //先保存角色权限
        this.saveBatch(savedAuthList);
        //再更新角色权限的缓存
        logoutByRoleId(id);
        return true;
    }

    @Override
    public RoleAuthVo getRoleAuth(Long id) {

        //如果是超级管理员 默认返回所有权限
        List<Menu> menus = menuMapper.selectList(Wrappers.lambdaQuery(Menu.class)
                .select(Menu::getId, Menu::getParentId)
                .eq(Menu::getEnabledMark, EnabledMark.ENABLED.getCode()));
        if (Objects.equals(id, GlobalConstant.SUPER_ADMIN_ROLE_ID)) {
            List<MenuButton> buttons = menuButtonMapper.selectList(Wrappers.lambdaQuery(MenuButton.class).select(MenuButton::getId));

            RoleAuthVo adminVo = new RoleAuthVo();
            adminVo.setMenuIds(menus.stream().map(Menu::getId).collect(Collectors.toList()));
            adminVo.setButtonIds(buttons.stream().map(MenuButton::getId).collect(Collectors.toList()));
            return adminVo;
        }

        //获取角色所有权限
        List<Authorize> authorizeList = authorizeMapper.selectList(Wrappers.lambdaQuery(Authorize.class).eq(Authorize::getRoleId, id));
        //将角色权限分为 菜单、按钮、列表字段和表单字段权限
        List<Long> menuIdList = new ArrayList<>();
        List<Long> btnIdList = new ArrayList<>();
        List<Long> colIdList = new ArrayList<>();
        List<Long> formIdList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(authorizeList)) {
            for (Authorize authorize : authorizeList) {
                Integer authorizeType = authorize.getAuthorizeType();
                Long authorizeId = authorize.getObjectId();
                if (authorizeType == AuthorizeType.MENU.getCode()) {
                    menuIdList.add(authorizeId);
                } else if (authorizeType == AuthorizeType.BUTTON.getCode()) {
                    btnIdList.add(authorizeId);
                } else if (authorizeType == AuthorizeType.COLUMN.getCode()) {
                    colIdList.add(authorizeId);
                } else if (authorizeType == AuthorizeType.FORM.getCode()) {
                    formIdList.add(authorizeId);
                }
            }
        }
        RoleAuthVo roleAuthVo = new RoleAuthVo();
        if (CollectionUtils.isNotEmpty(menuIdList)) {
            // 过滤掉父级菜单id
//            List<Menu> menuList = menuMapper.selectList(Wrappers.<Menu>query().lambda()
//                    .select(Menu::getParentId, Menu::getId)
//                    .in(Menu::getId, menuIdList));
            List<Menu> authMenuList = menus.stream().filter(menu -> menuIdList.contains(menu.getId())).collect(Collectors.toList());
            List<MenuTreeVo> menuTreeVos = BeanUtil.copyToList(authMenuList, MenuTreeVo.class);
            List<MenuTreeVo> menuTreeList = TreeUtil.build(menuTreeVos);
            Set<Long> parentIdSet = menus.stream().map(Menu::getParentId).collect(Collectors.toSet());
            buildLastChildIds(menuTreeList, roleAuthVo.getMenuIds(), parentIdSet);
        }
        if (CollectionUtils.isNotEmpty(btnIdList)) {
            // 过滤按钮id
            List<MenuButton> menuButtonList = menuButtonMapper.selectList(Wrappers.<MenuButton>query().lambda()
                    .select(MenuButton::getId).in(MenuButton::getId, btnIdList));
            roleAuthVo.setButtonIds(menuButtonList.stream().map(MenuButton::getId).collect(Collectors.toList()));
        }
        if (CollectionUtils.isNotEmpty(colIdList)) {
            // 过滤列表字段id
            List<MenuColumn> menuColumnList = menuColumnMapper.selectList(Wrappers.<MenuColumn>query().lambda()
                    .select(MenuColumn::getId).in(MenuColumn::getId, colIdList));
            roleAuthVo.setColumnIds(menuColumnList.stream().map(MenuColumn::getId).collect(Collectors.toList()));
        }
        if (CollectionUtils.isNotEmpty(formIdList)) {
            // 过滤掉按钮id
            List<MenuForm> menuList = menuFormMapper.selectList(Wrappers.<MenuForm>query().lambda()
                    .select(MenuForm::getParentId, MenuForm::getId));
            List<MenuForm> authMenuFormList = new ArrayList<>();
            Set<Long> parentIdSet = new LinkedHashSet<>();
            for (MenuForm menuForm : menuList) {
                if (formIdList.contains(menuForm.getId())) {
                    authMenuFormList.add(menuForm);
                }
                parentIdSet.add(menuForm.getParentId());
            }
            List<MenuFormTreeVo> menuFormTreeVos = BeanUtil.copyToList(authMenuFormList, MenuFormTreeVo.class);
            List<MenuFormTreeVo> menuFormTreeList = TreeUtil.build(menuFormTreeVos);
            buildLastChildIds(menuFormTreeList, roleAuthVo.getFormIds(), parentIdSet);
        }
        return roleAuthVo;
    }

    private <T extends ITreeNode<T, Long>> void buildLastChildIds(List<T> treeList, List<Long> idList, Set<Long> parentIdList) {
        if (CollectionUtils.isEmpty(treeList)) {
            return;
        }
        for (ITreeNode<T, Long> treeVo : treeList) {
            List<T> children = treeVo.getChildren();
            if (CollectionUtils.isNotEmpty(children)) {
                buildLastChildIds(children, idList, parentIdList);
            } else if (!parentIdList.contains(treeVo.getId())) {
                idList.add(treeVo.getId());
            }
        }
    }

    /**
     * 根据roleId 登出所有用户  用于：角色权限变化，所有拥有此权限的用户 必须全部重新登录
     *
     * @param id
     */
    private void logoutByRoleId(Long id) {
        List<UserRoleRelation> relations = userRoleRelationMapper.selectList(Wrappers.lambdaQuery(UserRoleRelation.class)
                .eq(UserRoleRelation::getRoleId, id).select(UserRoleRelation::getUserId));

        List<Long> currentRoleUser = relations.stream().map(UserRoleRelation::getUserId).collect(Collectors.toList());
        currentRoleUser.forEach(StpUtil::logout);
    }

    /**
     * 构建授权数据
     *
     * @param roleId        角色id
     * @param itemIdList    菜单id
     * @param itemType      菜单类型，0-菜单，1-按钮，2-列表字段，3-表单字段
     * @param authorizeList 构建的保存对象集合
     */
    private void buildAuthorize(Long roleId, List<Long> itemIdList, Integer itemType, List<Authorize> authorizeList) {
        if (CollectionUtils.isNotEmpty(itemIdList)) {
            for (Long itemId : itemIdList) {
                Authorize authorize = new Authorize();
                authorize.setAuthorizeType(itemType);
                authorize.setObjectId(itemId);
                authorize.setRoleId(roleId);
                authorizeList.add(authorize);
            }
        }
    }
}
