package cn.iocoder.yudao.module.system.service.tenant;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.tenant.config.TenantProperties;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
import cn.iocoder.yudao.module.employee.api.EmployeeApi;
import cn.iocoder.yudao.module.employee.api.dto.EmployeeCreateReqDTO;
import cn.iocoder.yudao.module.employee.api.dto.EmployeeQueryReqDTO;
import cn.iocoder.yudao.module.employee.api.dto.EmployeeRespDTO;
import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO;
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthLoginRespVO;
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO;
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuTenantRelateSaveReqVO;
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleSaveReqVO;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.*;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.tenant.UserTenantRelateSaveReqVO;
import cn.iocoder.yudao.module.system.convert.auth.AuthConvert;
import cn.iocoder.yudao.module.system.convert.tenant.TenantConvert;
import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantMapper;
import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
import cn.iocoder.yudao.module.system.enums.oauth2.OAuth2ClientConstants;
import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum;
import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum;
import cn.iocoder.yudao.module.system.service.auth.AdminAuthService;
import cn.iocoder.yudao.module.system.service.logger.LoginLogService;
import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService;
import cn.iocoder.yudao.module.system.service.permission.MenuService;
import cn.iocoder.yudao.module.system.service.permission.MenuTenantRelateService;
import cn.iocoder.yudao.module.system.service.permission.PermissionService;
import cn.iocoder.yudao.module.system.service.permission.RoleService;
import cn.iocoder.yudao.module.system.service.tenant.handler.TenantInfoHandler;
import cn.iocoder.yudao.module.system.service.tenant.handler.TenantMenuHandler;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import cn.iocoder.yudao.module.system.service.user.UserTenantRelateService;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

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

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
import static java.util.Collections.singleton;

/**
 * 租户 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
@Slf4j
public class TenantServiceImpl implements TenantService {

    @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
    @Autowired(required = false) // 由于 yudao.tenant.enable 配置项，可以关闭多租户的功能，所以这里只能不强制注入
    private TenantProperties tenantProperties;

    @Resource
    private TenantMapper tenantMapper;

    @Resource
    private TenantPackageService tenantPackageService;
    @Resource
    @Lazy // 延迟，避免循环依赖报错
    private AdminUserService userService;
    @Resource
    private RoleService roleService;
    @Resource
    private MenuService menuService;
    @Resource
    private PermissionService permissionService;
    @Resource
    private UserTenantRelateService userTenantRelateService;
    @Resource
    private EmployeeApi employeeApi;
    @Resource
    private LoginLogService loginLogService;
    @Resource
    private OAuth2TokenService oauth2TokenService;
    @Resource
    private AdminAuthService authService;
    @Resource
    private MenuTenantRelateService menuTenantRelateService;

    @Override
    public List<Long> getTenantIdList() {
        List<TenantDO> tenants = tenantMapper.selectList();
        return CollectionUtils.convertList(tenants, TenantDO::getId);
    }

    @Override
    public void validTenant(Long id) {
        TenantDO tenant = getTenant(id);
        if (tenant == null) {
            throw exception(TENANT_NOT_EXISTS);
        }
        if (tenant.getStatus().equals(CommonStatusEnum.DISABLE.getStatus())) {
            throw exception(TENANT_DISABLE, tenant.getName());
        }
        if (DateUtils.isExpired(tenant.getExpireTime())) {
            throw exception(TENANT_EXPIRE, tenant.getName());
        }
    }

    @Override
    @DSTransactional // 多数据源，使用 @DSTransactional 保证本地事务，以及数据源的切换
    public TenantSimpleRespVO createTenant(TenantSaveReqVO createReqVO) {
        // 校验租户名称是否重复
        validTenantNameDuplicate(createReqVO.getName(), null);
        // 校验租户域名是否重复
        validTenantWebsiteDuplicate(createReqVO.getWebsite(), null);
        // 校验套餐被禁用
//        TenantPackageDO tenantPackage = tenantPackageService.validTenantPackage(createReqVO.getPackageId());

        // 创建租户
        TenantDO tenant = BeanUtils.toBean(createReqVO, TenantDO.class);
        String corpId = IdUtil.fastSimpleUUID();
        tenant.setCorpId(corpId);
        tenant.setExpireTime(LocalDateTime.now().plusYears(10));// 默认设置10年有效期
        tenantMapper.insert(tenant);
        // 获得用户信息
        LoginUser user = SecurityFrameworkUtils.getLoginUser();
        // 自动根据账号创建对应人事信息，只保存姓名、头像、手机号
        AdminUserDO adminUserDO = userService.getUser(user.getId());
        if (adminUserDO == null) {
            throw exception(USER_NOT_EXISTS);
        }
//        // 更新用户信息当前租户
//        userService.updateUserTenantId(user.getId(), tenant.getId());
//        // 将这个用户所有租户关系设为不生效
//        userTenantRelateService.deactivateAllForUser(user.getId());
        // 插入用户租户关系
        userTenantRelateService.createUserTenantRelate(new UserTenantRelateSaveReqVO().setUserId(user.getId()).setTenantId(tenant.getId()).setActived(false));
        // 先判断该租户下员工信息是否存在，如果不存在则创建
        EmployeeRespDTO employeeRespDTO = employeeApi.getEmployee(new EmployeeQueryReqDTO().setUserId(user.getId()).setTenantId(tenant.getId()));
        if (employeeRespDTO == null) {
            // 创建员工信息
            employeeApi.createEmployee(new EmployeeCreateReqDTO().setUserId(user.getId()).setTenantId(tenant.getId()).setName(adminUserDO.getNickname()).setAvatar(adminUserDO.getAvatar()).setPhone(adminUserDO.getMobile()));
        } else {
            throw exception(USER_TENANT_EMPLOYEE_DUPLICATE, tenant.getName());
        }
        TenantUtils.execute(tenant.getId(), () -> {
            // 创建租户内置角色
            Long roleId = createSystemSuperAdminRole(tenant.getId());
            createSystemAdminRole(tenant.getId());
            createCommonEmployeeRole(tenant.getId());
            createDepartmentLeaderRole(tenant.getId());
            // 分配角色
            permissionService.assignUserRole(user.getId(), singleton(roleId));
            // 修改租户的管理员
            tenantMapper.updateById(new TenantDO().setId(tenant.getId()).setContactUserId(user.getId()));
        });
        TenantSimpleRespVO tenantSimpleRespVO = new TenantSimpleRespVO();
        tenantSimpleRespVO.setId(tenant.getId());
        tenantSimpleRespVO.setName(tenant.getName());
        tenantSimpleRespVO.setCorpId(corpId);
        return tenantSimpleRespVO;
    }

    @Override
    @DSTransactional // 多数据源，使用 @DSTransactional 保证本地事务，以及数据源的切换
    public TenantSimpleRespVO joinTenant(TenantJoinReqVO createReqVO) {
        // 根据租户邀请码查询租户信息
        TenantDO tenant = tenantMapper.selectByCorpId(createReqVO.getCorpId());
        if (tenant == null || tenant.getId() == null) {
            throw exception(TENANT_NOT_EXISTS);
        }
        // 获得用户信息
        LoginUser user = SecurityFrameworkUtils.getLoginUser();
        // 校验该用户是否在该租户中
        if (userTenantRelateService.checkUserHasTenant(user.getId(), tenant.getId())) {
            throw exception(USER_TENANT_EXISTS, tenant.getName());
        }
//        // 更新用户信息当前租户
//        userService.updateUserTenantId(user.getId(), tenant.getId());
//        // 将这个用户所有租户关系设为不生效
//        userTenantRelateService.deactivateAllForUser(user.getId());
        // 插入用户租户关系
        userTenantRelateService.createUserTenantRelate(new UserTenantRelateSaveReqVO().setUserId(user.getId()).setTenantId(tenant.getId()).setActived(false));
        // 自动根据账号创建对应人事信息，只保存姓名、头像、手机号
        AdminUserDO adminUserDO = userService.getUser(user.getId());
        if (adminUserDO == null) {
            throw exception(USER_NOT_EXISTS);
        }
        // 先判断该租户下员工信息是否存在，如果不存在则创建
        EmployeeRespDTO employeeRespDTO = employeeApi.getEmployee(new EmployeeQueryReqDTO().setUserId(user.getId()).setTenantId(tenant.getId()));
        if (employeeRespDTO == null) {
            // 创建员工信息
            employeeApi.createEmployee(new EmployeeCreateReqDTO().setUserId(user.getId()).setTenantId(tenant.getId()).setName(adminUserDO.getNickname()).setAvatar(adminUserDO.getAvatar()).setPhone(adminUserDO.getMobile()));
        } else {
            throw exception(USER_TENANT_EMPLOYEE_DUPLICATE, tenant.getName());
        }
        TenantUtils.execute(tenant.getId(), () -> {
            RoleDO roleDO = roleService.selectByName(RoleCodeEnum.COMMON_EMPLOYEE.getName());
            if (roleDO == null || roleDO.getId() == null) {
                throw exception(ROLE_NOT_EXISTS);
            }
            // 分配角色
            permissionService.assignUserRole(user.getId(), singleton(roleDO.getId()));
        });
        TenantSimpleRespVO tenantSimpleRespVO = new TenantSimpleRespVO();
        tenantSimpleRespVO.setId(tenant.getId());
        tenantSimpleRespVO.setName(tenant.getName());
        tenantSimpleRespVO.setCorpId(tenant.getCorpId());
        return tenantSimpleRespVO;
    }

    private Long createUser(Long roleId, TenantSaveReqVO createReqVO) {
        // 创建用户
        Long userId = userService.createUser(TenantConvert.INSTANCE.convert02(createReqVO));
        // 分配角色
        permissionService.assignUserRole(userId, singleton(roleId));
        return userId;
    }

    private Long createRole(TenantPackageDO tenantPackage) {
        // 创建角色
        RoleSaveReqVO reqVO = new RoleSaveReqVO();
        reqVO.setName(RoleCodeEnum.TENANT_ADMIN.getName()).setCode(RoleCodeEnum.TENANT_ADMIN.getCode())
                .setSort(0).setRemark("系统自动生成");
        Long roleId = roleService.createRole(reqVO, RoleTypeEnum.SYSTEM.getType());
        // 分配权限
        permissionService.assignRoleMenu(roleId, tenantPackage.getMenuIds());
        return roleId;
    }

    // 创建系统管理员：拥有所有OA磁贴权限和企业管理磁贴权限，只有本人数据权限，是给创建人默认授予的角色。
    private Long createSystemSuperAdminRole(Long tenantId) {
        // 创建角色
        RoleSaveReqVO reqVO = new RoleSaveReqVO();
        reqVO.setName(RoleCodeEnum.SYSTEM_SUPER_ADMIN.getName()).setCode(RoleCodeEnum.SYSTEM_SUPER_ADMIN.getCode())
                .setSort(0).setRemark("创建租户内置角色");
        Long roleId = roleService.createRole(reqVO, RoleTypeEnum.CUSTOM.getType());
        Set<Long> menuIds = new HashSet<>();
        // 获取OA菜单
        MenuDO oaMenu = menuService.selectByParentIdAndName(0L, "OA");
        // 获取其他菜单
        MenuDO otherMenu = menuService.selectByParentIdAndName(0L, "其他");
        // 获取企业管理菜单
        MenuDO manageMenu = menuService.selectByParentIdAndName(otherMenu.getId(), "企业管理");
        // 获取OA菜单下所有子级菜单ID
        Set<Long> oaChildrenMenuIds = this.collectAllMenuIds(oaMenu.getId());
        // 获取企业管理菜单下所有子级菜单ID
        Set<Long> manageChildrenMenuIds = this.collectAllMenuIds(manageMenu.getId());
        menuIds.add(oaMenu.getId());
        menuIds.addAll(oaChildrenMenuIds);
        menuIds.add(otherMenu.getId());
        menuIds.add(manageMenu.getId());
        menuIds.addAll(manageChildrenMenuIds);
        menuIds.stream()
                .forEach(menuId ->
                        menuTenantRelateService.createTenantRelate(new MenuTenantRelateSaveReqVO().setMenuId(menuId).setTenantId(tenantId))
                );
        // 分配菜单权限
        permissionService.assignRoleMenu(roleId, menuIds);
        // 分配数据权限
        permissionService.assignRoleDataScope(roleId, DataScopeEnum.SELF.getScope(), null);
        return roleId;
    }
    // 创建管理员：拥有所有磁贴权限，拥有全部数据权限，默认没有人拥有此角色，需要手动授予。
    private Long createSystemAdminRole(Long tenantId) {
        // 创建角色
        RoleSaveReqVO reqVO = new RoleSaveReqVO();
        reqVO.setName(RoleCodeEnum.SYSTEM_ADMIN.getName()).setCode(RoleCodeEnum.SYSTEM_ADMIN.getCode())
                .setSort(0).setRemark("创建租户内置角色");
        Long roleId = roleService.createRole(reqVO, RoleTypeEnum.CUSTOM.getType());
        // 获取菜单Ids
        List<MenuDO> menus = menuService.getMenuList(new MenuListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus()));
        Set<Long> menuIds = menus.stream()
                .map(MenuDO::getId) // 映射每个MenuDO对象到它的id
                .collect(Collectors.toSet()); // 收集到一个新的Set中
        // 分配菜单权限
        permissionService.assignRoleMenu(roleId, menuIds);
        // 分配数据权限
        permissionService.assignRoleDataScope(roleId, DataScopeEnum.ALL.getScope(), null);
        return roleId;
    }
    // 普通员工：拥有所有OA磁贴权限，只有本人数据权限，是加入企业时，默认授予的角色。
    private Long createCommonEmployeeRole(Long tenantId) {
        // 创建角色
        RoleSaveReqVO reqVO = new RoleSaveReqVO();
        reqVO.setName(RoleCodeEnum.COMMON_EMPLOYEE.getName()).setCode(RoleCodeEnum.COMMON_EMPLOYEE.getCode())
                .setSort(0).setRemark("创建租户内置角色");
        Long roleId = roleService.createRole(reqVO, RoleTypeEnum.CUSTOM.getType());
        Set<Long> menuIds = new HashSet<>();
        // 获取OA菜单
        MenuDO oaMenu = menuService.selectByParentIdAndName(0L, "OA");
        // 获取OA菜单下所有子级菜单ID
        Set<Long> oaChildrenMenuIds = this.collectAllMenuIds(oaMenu.getId());
        menuIds.add(oaMenu.getId());
        menuIds.addAll(oaChildrenMenuIds);
        // 分配菜单权限
        permissionService.assignRoleMenu(roleId, menuIds);
        // 分配数据权限
        permissionService.assignRoleDataScope(roleId, DataScopeEnum.SELF.getScope(), null);
        return roleId;
    }
    // 部门负责人：拥有所有OA磁贴权限，有本部门及以下数据权限，默认没有人拥有此角色，需要手动授予。
    private Long createDepartmentLeaderRole(Long tenantId) {
        // 创建角色
        RoleSaveReqVO reqVO = new RoleSaveReqVO();
        reqVO.setName(RoleCodeEnum.DEPARTMENT_LEADER.getName()).setCode(RoleCodeEnum.DEPARTMENT_LEADER.getCode())
                .setSort(0).setRemark("创建租户内置角色");
        Long roleId = roleService.createRole(reqVO, RoleTypeEnum.CUSTOM.getType());
        Set<Long> menuIds = new HashSet<>();
        // 获取OA菜单
        MenuDO oaMenu = menuService.selectByParentIdAndName(0L, "OA");
        // 获取OA菜单下所有子级菜单ID
        Set<Long> oaChildrenMenuIds = this.collectAllMenuIds(oaMenu.getId());
        menuIds.add(oaMenu.getId());
        menuIds.addAll(oaChildrenMenuIds);
        // 分配菜单权限
        permissionService.assignRoleMenu(roleId, menuIds);
        // 分配数据权限
        permissionService.assignRoleDataScope(roleId, DataScopeEnum.DEPT_AND_CHILD.getScope(), null);
        return roleId;
    }

    @Override
    @DSTransactional // 多数据源，使用 @DSTransactional 保证本地事务，以及数据源的切换
    public void updateTenant(TenantSaveReqVO updateReqVO) {
        // 1.1 获得用户信息
        AdminUserDO user = userService.getUser(getLoginUserId());
        if (user == null) {
            throw exception(USER_NOT_EXISTS);
        }
        AdminUserDO adminUserDO = userService.getUser(user.getId());
        if (adminUserDO == null || adminUserDO.getTenantId() == null) {
            throw exception(USER_NOT_EXISTS);
        }
        if (updateReqVO.getId() == null) {
            updateReqVO.setId(adminUserDO.getTenantId());
        }
        // 校验存在
        TenantDO tenant = validateUpdateTenant(updateReqVO.getId());
        // 校验租户名称是否重复
        validTenantNameDuplicate(updateReqVO.getName(), updateReqVO.getId());
        // 校验租户域名是否重复
        validTenantWebsiteDuplicate(updateReqVO.getWebsite(), updateReqVO.getId());
        // 校验套餐被禁用
//        TenantPackageDO tenantPackage = tenantPackageService.validTenantPackage(updateReqVO.getPackageId());

        // 更新租户
        TenantDO updateObj = BeanUtils.toBean(updateReqVO, TenantDO.class);
        tenantMapper.updateById(updateObj);
        // 如果套餐发生变化，则修改其角色的权限
//        if (ObjectUtil.notEqual(tenant.getPackageId(), updateReqVO.getPackageId())) {
//            updateTenantRoleMenu(tenant.getId(), tenantPackage.getMenuIds());
//        }
    }

    @Override
    @DSTransactional // 多数据源，使用 @DSTransactional 保证本地事务，以及数据源的切换
    public AuthLoginRespVO changeTenant(TenantJoinReqVO createReqVO) {
        // 根据租户邀请码查询租户信息
        TenantDO tenant = tenantMapper.selectByCorpId(createReqVO.getCorpId());
        if (tenant == null || tenant.getId() == null) {
            throw exception(TENANT_NOT_EXISTS);
        }
        // 获得用户信息
        LoginUser user = SecurityFrameworkUtils.getLoginUser();
        if (user == null) {
            throw exception(USER_NOT_EXISTS);
        }
        AdminUserDO adminUserDO = userService.getUser(user.getId());
        // 校验该用户是否在该租户中
        if (!userTenantRelateService.checkUserHasTenant(user.getId(), tenant.getId())) {
            throw exception(USER_NOT_IN_TENANT);
        }
        // 更新用户信息当前租户
        userService.updateUserTenantId(user.getId(), tenant.getId());
        // 将这个用户所有租户关系设为不生效
        userTenantRelateService.deactivateAllForUser(user.getId());
        // 将这个用户的这个租户关系设为生效
        userTenantRelateService.activateForUser(new UserTenantRelateSaveReqVO().setUserId(user.getId()).setTenantId(tenant.getId()));
        authService.logout(createReqVO.getToken(), LoginLogTypeEnum.LOGOUT_CHANGE_TENANT.getType());
        return createTokenAfterLoginSuccess(adminUserDO.getId(), adminUserDO.getUsername(), LoginLogTypeEnum.LOGIN_CHANGE_TENANT, tenant.getId());
    }

    private void validTenantNameDuplicate(String name, Long id) {
        TenantDO tenant = tenantMapper.selectByName(name);
        if (tenant == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同名字的租户
        if (id == null) {
            throw exception(TENANT_NAME_DUPLICATE, name);
        }
        if (!tenant.getId().equals(id)) {
            throw exception(TENANT_NAME_DUPLICATE, name);
        }
    }

    private void validTenantWebsiteDuplicate(String website, Long id) {
        if (StrUtil.isEmpty(website)) {
            return;
        }
        TenantDO tenant = tenantMapper.selectByWebsite(website);
        if (tenant == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同名字的租户
        if (id == null) {
            throw exception(TENANT_WEBSITE_DUPLICATE, website);
        }
        if (!tenant.getId().equals(id)) {
            throw exception(TENANT_WEBSITE_DUPLICATE, website);
        }
    }

    @Override
    @DSTransactional
    public void updateTenantRoleMenu(Long tenantId, Set<Long> menuIds) {
        TenantUtils.execute(tenantId, () -> {
            // 获得所有角色
            List<RoleDO> roles = roleService.getRoleList();
            roles.forEach(role -> Assert.isTrue(tenantId.equals(role.getTenantId()), "角色({}/{}) 租户不匹配",
                    role.getId(), role.getTenantId(), tenantId)); // 兜底校验
            // 重新分配每个角色的权限
            roles.forEach(role -> {
                // 如果是租户管理员，重新分配其权限为租户套餐的权限
                if (Objects.equals(role.getCode(), RoleCodeEnum.TENANT_ADMIN.getCode())) {
                    permissionService.assignRoleMenu(role.getId(), menuIds);
                    log.info("[updateTenantRoleMenu][租户管理员({}/{}) 的权限修改为({})]", role.getId(), role.getTenantId(), menuIds);
                    return;
                }
                // 如果是其他角色，则去掉超过套餐的权限
                Set<Long> roleMenuIds = permissionService.getRoleMenuListByRoleId(role.getId());
                roleMenuIds = CollUtil.intersectionDistinct(roleMenuIds, menuIds);
                permissionService.assignRoleMenu(role.getId(), roleMenuIds);
                log.info("[updateTenantRoleMenu][角色({}/{}) 的权限修改为({})]", role.getId(), role.getTenantId(), roleMenuIds);
            });
        });
    }

    @Override
    public void deleteTenant(Long id) {
        // 校验存在
        validateUpdateTenant(id);
        // 删除
        tenantMapper.deleteById(id);
    }

    private TenantDO validateUpdateTenant(Long id) {
        TenantDO tenant = tenantMapper.selectById(id);
        if (tenant == null) {
            throw exception(TENANT_NOT_EXISTS);
        }
        // 内置租户，不允许删除
        if (isSystemTenant(tenant)) {
            throw exception(TENANT_CAN_NOT_UPDATE_SYSTEM);
        }
        return tenant;
    }

    @Override
    public TenantDO getTenant(Long id) {
        return tenantMapper.selectById(id);
    }

    @Override
    public List<TenantRespVO> getOwnCreateTenants() {
        // 获得用户信息
        LoginUser user = SecurityFrameworkUtils.getLoginUser();
        if (user == null) {
            throw exception(USER_NOT_EXISTS);
        }
        return tenantMapper.getOwnCreateTenants(user.getId());
    }

    @Override
    public List<TenantRespVO> getOwnJoinTenants() {
        // 获得用户信息
        LoginUser user = SecurityFrameworkUtils.getLoginUser();
        if (user == null) {
            throw exception(USER_NOT_EXISTS);
        }
        return tenantMapper.getOwnJoinTenants(user.getId());
    }

    @Override
    public PageResult<TenantDO> getTenantPage(TenantPageReqVO pageReqVO) {
        return tenantMapper.selectPage(pageReqVO);
    }

    @Override
    public TenantDO getTenantByName(String name) {
        return tenantMapper.selectByName(name);
    }

    @Override
    public TenantDO getTenantByWebsite(String website) {
        return tenantMapper.selectByWebsite(website);
    }

    @Override
    public Long getTenantCountByPackageId(Long packageId) {
        return tenantMapper.selectCountByPackageId(packageId);
    }

    @Override
    public List<TenantDO> getTenantListByPackageId(Long packageId) {
        return tenantMapper.selectListByPackageId(packageId);
    }

    @Override
    public void handleTenantInfo(TenantInfoHandler handler) {
        // 如果禁用，则不执行逻辑
        if (isTenantDisable()) {
            return;
        }
        // 获得租户
        TenantDO tenant = getTenant(TenantContextHolder.getRequiredTenantId());
        // 执行处理器
        handler.handle(tenant);
    }

    @Override
    public void handleTenantMenu(TenantMenuHandler handler) {
        // 如果禁用，则不执行逻辑
        if (isTenantDisable()) {
            return;
        }
        // 获得租户，然后获得菜单
        TenantDO tenant = getTenant(TenantContextHolder.getRequiredTenantId());
        Set<Long> menuIds;
        if (isSystemTenant(tenant)) { // 系统租户，菜单是全量的
            menuIds = CollectionUtils.convertSet(menuService.getMenuList(), MenuDO::getId);
        } else {
//            menuIds = tenantPackageService.getTenantPackage(tenant.getPackageId()).getMenuIds();
            // 获取菜单Ids
            List<MenuDO> menus = menuService.getMenuList(new MenuListReqVO().setTenantId(0L).setStatus(CommonStatusEnum.ENABLE.getStatus()));
            menuIds = menus.stream()
                    .map(MenuDO::getId) // 映射每个MenuDO对象到它的id
                    .collect(Collectors.toSet()); // 收集到一个新的Set中
        }
        // 执行处理器
        handler.handle(menuIds);
    }

    private static boolean isSystemTenant(TenantDO tenant) {
        return Objects.equals(tenant.getPackageId(), TenantDO.PACKAGE_ID_SYSTEM);
    }

    private boolean isTenantDisable() {
        return tenantProperties == null || Boolean.FALSE.equals(tenantProperties.getEnable());
    }

    private AuthLoginRespVO createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType, Long tenantId) {
        // 插入登陆日志
        createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS);
        // 创建访问令牌
        OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, getUserType().getValue(),
                OAuth2ClientConstants.CLIENT_ID_DEFAULT, null, tenantId);
        // 构建返回结果
        return AuthConvert.INSTANCE.convert(accessTokenDO);
    }

    private void createLoginLog(Long userId, String username,
                                LoginLogTypeEnum logTypeEnum, LoginResultEnum loginResult) {
        // 插入登录日志
        LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO();
        reqDTO.setLogType(logTypeEnum.getType());
        reqDTO.setTraceId(TracerUtils.getTraceId());
        reqDTO.setUserId(userId);
        reqDTO.setUserType(getUserType().getValue());
        reqDTO.setUsername(username);
        reqDTO.setUserAgent(ServletUtils.getUserAgent());
        reqDTO.setUserIp(ServletUtils.getClientIP());
        reqDTO.setResult(loginResult.getResult());
        loginLogService.createLoginLog(reqDTO);
        // 更新最后登录时间
        if (userId != null && Objects.equals(LoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) {
            userService.updateUserLogin(userId, ServletUtils.getClientIP());
        }
    }

    private UserTypeEnum getUserType() {
        return UserTypeEnum.ADMIN;
    }

    // 递归方法，收集所有menuId并放入Set中
    private Set<Long> collectAllMenuIds(Long parentId) {
        Set<Long> menuIds = new HashSet<>();

        // 获取当前parentId的直接子菜单
        List<MenuDO> childrenMenus = menuService.getMenuList(new MenuListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus()).setParentId(parentId));

        // 将当前层级的menuId添加到Set中
        for (MenuDO menu : childrenMenus) {
            menuIds.add(menu.getId()); // 假设Menu类有一个getId()方法返回menuId

            // 递归收集子菜单的menuId
            Set<Long> subMenuIds = collectAllMenuIds(menu.getId());
            menuIds.addAll(subMenuIds); // 将子菜单的menuId也添加到Set中
        }

        return menuIds;
    }

}
