package com.gxwebsoft.common.system.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gxwebsoft.common.core.Constants;
import com.gxwebsoft.common.core.utils.CommonUtil;
import com.gxwebsoft.common.core.utils.DomainUtil;
import com.gxwebsoft.common.core.utils.RedisUtil;
import com.gxwebsoft.common.system.entity.*;
import com.gxwebsoft.common.system.mapper.MenuMapper;
import com.gxwebsoft.common.system.mapper.TenantMapper;
import com.gxwebsoft.common.system.param.MenuParam;
import com.gxwebsoft.common.system.service.*;
import com.gxwebsoft.common.system.param.TenantParam;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 租户Service实现
 *
 * @author 科技小王子
 * @since 2023-07-17 17:49:53
 */
@Service
public class TenantServiceImpl extends ServiceImpl<TenantMapper, Tenant> implements TenantService {
  // cms建站权限标识前缀
  private static final String CMS_AUTHORITY_PREFIX = "cms:";

  @Resource
  private CompanyService companyService;
  @Resource
  private MenuService menuService;
  @Resource
  private MenuMapper menuMapper;
  @Resource
  private RoleMenuService roleMenuService;
  @Resource
  private RoleService roleService;
  @Resource
  private UserRoleService userRoleService;
  @Resource
  private DictService dictService;
  @Resource
  private DictDataService dictDataService;
  @Resource
  private EmailRecordService emailRecordService;
  @Resource
  private UserService userService;
  @Resource
  private RedisUtil redisUtil;

    @Override
    public PageResult<Tenant> pageRel(TenantParam param) {
        PageParam<Tenant, TenantParam> page = new PageParam<>(param);
        page.setDefaultOrder("sort_number asc, create_time desc");
        List<Tenant> list = baseMapper.selectPageRel(page, param);
        return new PageResult<>(list, page.getTotal());
    }

    @Override
    public List<Tenant> listRel(TenantParam param) {
        List<Tenant> list = baseMapper.selectListRel(param);
        // 排序
        PageParam<Tenant, TenantParam> page = new PageParam<>();
        //page.setDefaultOrder("create_time desc");
        return page.sortRecords(list);
    }

    @Override
    public Tenant getByIdRel(Integer tenantId) {
        TenantParam param = new TenantParam();
        return param.getOne(baseMapper.selectListRel(param));
    }

    @Override
    public Company initialization(Company company) {
      // 添加默认字典
      Dict dict = new Dict();
      dict.setDictName("性别");
      dict.setDictCode("sex");
      dict.setTenantId(company.getTid());
      dictService.save(dict);
      DictData dictData = new DictData();
      dictData.setDictId(dict.getDictId());
      dictData.setDictDataName("男");
      dictData.setDictDataCode("1");
      dictData.setSortNumber(100);
      dictData.setTenantId(company.getTid());
      dictDataService.save(dictData);
      dictData.setDictDataName("女");
      dictData.setDictDataCode("2");
      dictData.setTenantId(company.getTid());
      dictDataService.save(dictData);
      dict.setDictName("机构类型");
      dict.setDictCode("organizationType");
      dict.setTenantId(company.getTid());
      dictService.save(dict);
      dictData.setDictId(dict.getDictId());
      dictData.setDictDataName("公司");
      dictData.setDictDataCode("1");
      dictData.setTenantId(company.getTid());
      dictDataService.save(dictData);
      dictData.setDictId(dict.getDictId());
      dictData.setDictDataName("部门");
      dictData.setDictDataCode("2");
      dictData.setTenantId(company.getTid());
      dictDataService.save(dictData);

      // 添加超级管理员
      User superAdmin = new User();
      superAdmin.setUsername("superAdmin");
      superAdmin.setNickname(company.getShortName());
      superAdmin.setPhone(company.getPhone());
      superAdmin.setIsSuperAdmin(true);
      superAdmin.setIsAdmin(true);
      superAdmin.setEmail(company.getEmail());
      superAdmin.setTemplateId(company.getTemplateId());
      superAdmin.setRealName(company.getBusinessEntity());
      superAdmin.setPassword(userService.encodePassword("$2a$10$iMsEmh.rPlzwy/SVe6KW3.62vlwqMJpibhCF9jYN.fMqxdqymzMzu"));
      if (company.getPassword() != null) {
        superAdmin.setPassword(userService.encodePassword(company.getPassword()));
      }
      superAdmin.setTenantId(company.getTid());
      if(company.getTemplateId() != null){
        superAdmin.setTemplateId(company.getTemplateId());
      }
      // 企业资源配置
      company.setTenantId(company.getTid());
      company.setShortName(company.getShortName());
      company.setPhone(company.getPhone());
      company.setMembers(20);
      company.setServerUrl("https://server.websoft.top");
      company.setModulesUrl("https://cms-api.websoft.top");
      company.setSocketUrl("wss://server.gxwebsoft.com");
      company.setAdminUrl(DomainUtil.getAdminUrl(company.getTid().toString()));
      company.setWebsiteUrl(DomainUtil.getSiteUrl(company.getTid().toString()));
      company.setVersion(10);
      company.setIndustryParent("");
      company.setIndustryChild("");
      company.setDepartments(10);
      company.setDepartments(10);
      company.setStorageMax(524288000L);
      company.setAuthoritative(true);
      company.setUsers(2);
      company.setClicks(1L);
      company.setLikes(0L);
      company.setTid(company.getTid());
      company.setCompanyType("企业");
      company.setEmail(company.getEmail());
      company.setUserId(superAdmin.getUserId());
      company.setExpirationTime(DateUtil.nextMonth());

      final boolean save = companyService.save(company);
      superAdmin.setCompanyId(company.getCompanyId());

      boolean result = userService.save(superAdmin);
      Integer superAdminUserId = superAdmin.getUserId();

      // 创建角色
      if (result) {
        Role role = new Role();
        role.setRoleName("超级管理员");
        role.setRoleCode("superAdmin");
        role.setComments("超级管理员");
        role.setTenantId(company.getTid());
        roleService.save(role);

        // 保存超级管理员角色ID
        Integer superAdminRoleId = role.getRoleId();
        role.setRoleName("管理员");
        role.setRoleCode("admin");
        role.setComments("管理员");
        roleService.save(role);
        role.setRoleName("注册用户");
        role.setRoleCode("user");
        role.setComments("普通注册用户");
        roleService.save(role);
//        role.setRoleName("商户账号");
//        role.setRoleCode("merchantClerk");
//        role.setRoleName("管理人员/店员");
//        Integer guestRoleId = role.getRoleId();

        // 添加管理员账号
//        User admin = new User();
//        admin.setTenantId(company.getTid());
//        admin.setUsername("admin");
//        admin.setNickname("管理员");
//        admin.setPassword(userService.encodePassword(CommonUtil.randomUUID16()));
//        userService.save(admin);

        // 添加超管用户角色
        UserRole userRole = new UserRole();
        userRole.setUserId(superAdminUserId);
        userRole.setRoleId(superAdminRoleId);
        userRole.setTenantId(company.getTid());
        boolean resultUserRole = userRoleService.save(userRole);

        // 添加游客用户角色
//        userRole.setUserId(www.getUserId());
//        userRole.setRoleId(guestRoleId);
//        boolean resultUserRole = userRoleService.save(userRole);

        // 添加系统菜单
        if (resultUserRole) {
          Menu menu = new Menu();
          menu.setMenuType(0);
          menu.setParentId(0);
          menu.setHide(1);
          menu.setTitle("扩展插件");
          menu.setPath("/system/plug");
          menu.setComponent("/system/plug");
          menu.setIcon("AppstoreAddOutlined");
          menu.setAuthority("sys:plug:list");
          menu.setSortNumber(999);
          menu.setTenantId(company.getTid());
          menuService.save(menu);
          // 10.系统管理
          menu.setTitle("系统管理");
          menu.setParentId(0);
          menu.setPath("/system");
          menu.setIcon("setting-outlined");
          menu.setSortNumber(0);
          menu.setHide(0);
          menu.setTenantId(company.getTid());
          menuService.save(menu);
          Integer parentId = menu.getMenuId();
          menu.setParentId(menu.getMenuId());
          menu.setTitle("系统信息");
          menu.setPath("/system/profile");
          menu.setComponent("/system/profile");
          menu.setIcon("AuditOutlined");
          menu.setAuthority("sys:company:profile");
          menu.setSortNumber(1);
          menu.setHide(1);
          menuService.save(menu);
          Integer profileParentId = menu.getMenuId();
          menu.setParentId(profileParentId);
          menu.setMenuType(1);
          menu.setTitle("编辑");
          menu.setIcon("");
          menu.setAuthority("sys:company:update");
          menuService.save(menu);
          menu.setMenuType(0);
          menu.setParentId(parentId);
          menu.setTitle("菜单管理");
          menu.setPath("/system/menu");
          menu.setComponent("/system/menu");
          menu.setIcon("appstore-outlined");
          menu.setHide(0);
          menu.setAuthority("");
          menu.setSortNumber(2);
          menuService.save(menu);
          menu.setMenuType(0);
          menu.setParentId(parentId);
          menu.setTitle("角色管理");
          menu.setPath("/system/role");
          menu.setComponent("/system/role");
          menu.setIcon("idcard-outlined");
          menu.setAuthority("");
          menu.setSortNumber(3);
          menuService.save(menu);
          menu.setTitle("用户管理");
          menu.setPath("/system/user");
          menu.setComponent("/system/user");
          menu.setIcon("team-outlined");
          menu.setSortNumber(4);
          menuService.save(menu);
          Integer userParentId = menu.getMenuId();
          menu.setParentId(userParentId);
          menu.setMenuType(1);
          menu.setTitle("查询");
          menu.setIcon("");
          menu.setAuthority("sys:user:list");
          menuService.save(menu);
          menu.setParentId(userParentId);
          menu.setTitle("添加");
          menu.setAuthority("sys:user:save");
          menuService.save(menu);
          menu.setParentId(userParentId);
          menu.setTitle("修改");
          menu.setAuthority("sys:user:update");
          menuService.save(menu);
          menu.setParentId(userParentId);
          menu.setTitle("删除");
          menu.setAuthority("sys:user:remove");
          menuService.save(menu);
          menu.setMenuType(0);
          menu.setParentId(parentId);
          menu.setTitle("部门管理");
          menu.setPath("/system/organization");
          menu.setComponent("/system/organization");
          menu.setIcon("bank-outlined");
          menu.setAuthority("");
          menu.setSortNumber(4);
          menuService.save(menu);
          Integer orgParentId = menu.getMenuId();
          menu.setParentId(orgParentId);
          menu.setMenuType(1);
          menu.setPath("");
          menu.setComponent("");
          menu.setIcon("");
          menu.setTitle("查询");
          menu.setAuthority("sys:org:list");
          menuService.save(menu);
          menu.setParentId(orgParentId);
          menu.setTitle("添加");
          menu.setAuthority("sys:org:save");
          menuService.save(menu);
          menu.setParentId(orgParentId);
          menu.setTitle("修改");
          menu.setAuthority("sys:org:update");
          menuService.save(menu);
          menu.setParentId(orgParentId);
          menu.setTitle("删除");
          menu.setAuthority("sys:org:remove");
          menuService.save(menu);
          Integer roleParentId = menu.getMenuId();
          menu.setParentId(roleParentId);
          menu.setMenuType(1);
          menu.setIcon("");
          menu.setPath("");
          menu.setComponent("");
          menu.setTitle("查询");
          menu.setAuthority("sys:role:list");
          menuService.save(menu);
          menu.setParentId(roleParentId);
          menu.setTitle("添加");
          menu.setAuthority("sys:role:save");
          menuService.save(menu);
          menu.setParentId(roleParentId);
          menu.setTitle("修改");
          menu.setAuthority("sys:role:update");
          menuService.save(menu);
          menu.setParentId(roleParentId);
          menu.setTitle("删除");
          menu.setAuthority("sys:role:remove");
          menuService.save(menu);
          Integer menuParentId = menu.getMenuId();
          menu.setParentId(menuParentId);
          menu.setMenuType(1);
          menu.setTitle("查询");
          menu.setAuthority("sys:menu:list");
          menu.setIcon("");
          menu.setPath("");
          menu.setComponent("");
          menuService.save(menu);
          menu.setParentId(menuParentId);
          menu.setTitle("添加");
          menu.setAuthority("sys:menu:save");
          menuService.save(menu);
          menu.setParentId(menuParentId);
          menu.setTitle("修改");
          menu.setAuthority("sys:menu:update");
          menuService.save(menu);
          menu.setParentId(menuParentId);
          menu.setTitle("删除");
          menu.setAuthority("sys:menu:remove");
          menuService.save(menu);
          menu.setMenuType(0);
          menu.setParentId(parentId);
          menu.setTitle("字典管理");
          menu.setPath("/system/dict");
          menu.setComponent("/system/dict");
          menu.setIcon("profile-outlined");
          menu.setAuthority("");
          menu.setSortNumber(6);
          menuService.save(menu);
          Integer dictParentId = menu.getMenuId();
          menu.setParentId(dictParentId);
          menu.setMenuType(1);
          menu.setTitle("查询");
          menu.setAuthority("sys:dict:list");
          menu.setIcon("");
          menu.setPath("");
          menu.setComponent("");
          menuService.save(menu);
          menu.setParentId(dictParentId);
          menu.setTitle("添加");
          menu.setAuthority("sys:dict:save");
          menuService.save(menu);
          menu.setParentId(dictParentId);
          menu.setTitle("修改");
          menu.setAuthority("sys:dict:update");
          menuService.save(menu);
          menu.setParentId(dictParentId);
          menu.setTitle("删除");
          menu.setAuthority("sys:dict:remove");
          menuService.save(menu);
          menu.setMenuType(0);
          menu.setParentId(parentId);
          menu.setTitle("登录日志");
          menu.setPath("/system/login-record");
          menu.setComponent("/system/login-record");
          menu.setIcon("calendar-outlined");
          menu.setAuthority("sys:login-record:list");
          menu.setSortNumber(7);
          menuService.save(menu);
          menu.setParentId(parentId);
          menu.setTitle("文件管理");
          menu.setPath("/system/file");
          menu.setComponent("/system/file");
          menu.setIcon("folder-outlined");
          menu.setAuthority("");
          menu.setSortNumber(6);
          menuService.save(menu);
          Integer fileParentId = menu.getMenuId();
          menu.setParentId(fileParentId);
          menu.setMenuType(1);
          menu.setTitle("查看记录");
          menu.setPath("");
          menu.setComponent("");
          menu.setIcon("");
          menu.setAuthority("sys:file:list");
          menuService.save(menu);
          menu.setParentId(fileParentId);
          menu.setTitle("上传文件");
          menu.setAuthority("sys:file:upload");
          menuService.save(menu);
          menu.setParentId(fileParentId);
          menu.setTitle("修改文件");
          menu.setAuthority("sys:file:update");
          menuService.save(menu);
          menu.setParentId(fileParentId);
          menu.setTitle("删除文件");
          menu.setAuthority("sys:file:remove");
          menuService.save(menu);
          menu.setParentId(parentId);
          menu.setTitle("秘钥管理");
          menu.setPath("/system/access-key");
          menu.setComponent("/system/access-key");
          menu.setIcon("KeyOutlined");
          menu.setAuthority("sys:accessKey:list");
          menu.setSortNumber(8);
          menuService.save(menu);
          menu.setMenuType(0);
          menu.setParentId(parentId);
          menu.setTitle("扩展插件");
          menu.setPath("/system/plug");
          menu.setComponent("/system/plug");
          menu.setIcon("AppstoreAddOutlined");
          menu.setAuthority("sys:plug:list");
          menu.setSortNumber(9);
          menuService.save(menu);
          Integer plugParentId = menu.getMenuId();
          menu.setParentId(plugParentId);
          menu.setMenuType(1);
          menu.setTitle("查询");
          menu.setAuthority("sys:dict:list");
          menu.setIcon("");
          menu.setPath("");
          menu.setComponent("");
          menuService.save(menu);
          menu.setParentId(plugParentId);
          menu.setTitle("发布插件");
          menu.setAuthority("sys:plug:save");
          menuService.save(menu);
          menu.setParentId(plugParentId);
          menu.setTitle("更新插件");
          menu.setAuthority("sys:plug:update");
          menuService.save(menu);
          menu.setParentId(plugParentId);
          menu.setTitle("删除插件");
          menu.setAuthority("sys:plus:remove");
          menuService.save(menu);
          menu.setParentId(plugParentId);
          menu.setTitle("安装插件");
          menu.setAuthority("sys:plug:save");
          menuService.save(menu);
          menu.setMenuType(0);
          menu.setParentId(parentId);
          menu.setTitle("系统设置");
          menu.setPath("/system/setting");
          menu.setComponent("/system/setting");
          menu.setIcon("setting-outlined");
          menu.setAuthority("sys:setting:save");
          menu.setSortNumber(10);
          menuService.save(menu);
          menu.setParentId(parentId);
          menu.setTitle("用户信息");
          menu.setPath("/system/user-info");
          menu.setComponent("/system/user-info");
          menu.setIcon("team-outlined");
          menu.setAuthority("");
          menu.setHide(1);
          menu.setMenuType(0);
          menu.setSortNumber(9);
          menuService.save(menu);
          Integer userInfoParentId = menu.getMenuId();
          menu.setParentId(userInfoParentId);
          menu.setMenuType(1);
          menu.setTitle("修改个人密码");
          menu.setAuthority("sys:auth:password");
          menu.setIcon("");
          menu.setPath("");
          menu.setComponent("");
          menuService.save(menu);
          menu.setParentId(userInfoParentId);
          menu.setTitle("修改个人资料");
          menu.setAuthority("sys:auth:user");
          menuService.save(menu);
          // 1.控制台
//        menu.setParentId(0);
//        menu.setTitle("管理首页");
//        menu.setPath("/dashboard");
//        menu.setIcon("home-outlined");
//        menu.setComponent("/dashboard/workplace");
//        menu.setAuthority("");
//        menu.setSortNumber(1);
//        menu.setHide(0);
//        menu.setMenuType(0);
//        menuService.save(menu);

          // 个人中心
          menu.setParentId(0);
          menu.setTitle("个人中心");
          menu.setPath("/user-center");
          menu.setIcon("UserOutlined");
          menu.setComponent("");
          menu.setAuthority("");
          menu.setMenuType(0);
          menu.setHide(1);
          menu.setSortNumber(999);
          menuService.save(menu);
          Integer userCenterParentId = menu.getMenuId();
          menu.setTitle("个人资料");
          menu.setPath("/user/profile");
          menu.setComponent("/user/profile");
          menu.setIcon("IdcardOutlined");
          menu.setParentId(userCenterParentId);
          menu.setMenuType(0);
          menu.setSortNumber(0);
          menuService.save(menu);
          Integer userProfileParentId = menu.getMenuId();
          menu.setParentId(userProfileParentId);
          menu.setMenuType(1);
          menu.setIcon("");
          menu.setPath("");
          menu.setComponent("");
          menu.setTitle("修改资料");
          menu.setSortNumber(0);
          menu.setAuthority("sys:auth:user");
          menuService.save(menu);
          menu.setTitle("修改密码");
          menu.setAuthority("sys:auth:password");
          menuService.save(menu);

          menu.setTitle("上传头像");
          menu.setAuthority("sys:file:upload");
          menuService.save(menu);
          menu.setTitle("预览头像");
          menu.setAuthority("sys:file:list");
          menuService.save(menu);
          menu.setTitle("保存头像");
          menu.setAuthority("sys:user:update");
          menuService.save(menu);
          menu.setTitle("我的消息");
          menu.setPath("/user/notice");
          menu.setComponent("/user/notice");
          menu.setIcon("sound-outlined");
          menu.setParentId(userCenterParentId);
          menu.setMenuType(0);
          menuService.save(menu);
          Integer userNoticeParentId = menu.getMenuId();
          menu.setParentId(userNoticeParentId);
          menu.setTitle("列表");
          menu.setAuthority("sys:notice:list");
          menu.setSortNumber(0);
          menu.setMenuType(1);
          menu.setIcon("");
          menu.setPath("");
          menu.setComponent("");
          menuService.save(menu);
          menu.setTitle("添加");
          menu.setAuthority("sys:notice:save");
          menuService.save(menu);
          menu.setTitle("编辑");
          menu.setAuthority("sys:notice:update");
          menuService.save(menu);
          menu.setTitle("删除");
          menu.setAuthority("sys:notice:remove");
          menuService.save(menu);
          menu.setParentId(userCenterParentId);
          menu.setTitle("用户注册");
          menu.setAuthority("sys:user:save");
          menuService.save(menu);
          menu.setTitle("字典查询");
          menu.setAuthority("sys:dict:list");

          boolean resultMenu = menuService.save(menu);
          // 添加菜单ID到超级管理员所属角色ID
          if (resultMenu) {
            saveRedis(company);
          }
        }

      }
      // 发送邮件通知
      String title = "恭喜！您的应用已创建成功";
      String appUrl = "\r\n应用地址：" + DomainUtil.getSiteUrl(company.getTid().toString());
      String appName = "\r\n应用名称：" + company.getShortName();
      String adminUrl = "\r\n后台管理：" + DomainUtil.getAdminUrl(company.getTid().toString());
      String account = "\r\n账号：admin";
      String password = "\r\n密码：" + company.getPassword();
      String content = title + appUrl + appName + adminUrl + account + password;
      // 发送邮件通知
      if (company.getEmail() != null) {
        emailRecordService.sendEmail(title, content, company.getEmail(), company.getTid());
      }
      return company;
    }



  // 缓存租户信息
  private void saveRedis(Company tenant) {
    String key = "tenant:" + tenant.getTenantId();
    if (StrUtil.isEmpty(tenant.getTenantCode())) {
      tenant.setTenantCode(CommonUtil.randomUUID16());
    }
    redisUtil.set(key, tenant);
  }

  @Override
  public boolean destructionAll(Integer tenantId){
    if (baseMapper.destructionAll(tenantId)) {
      return true;
    }
    return false;
  }

  @Override
  public Tenant getByCodeRel(String code) {
    TenantParam param = new TenantParam();
    param.setTenantCode(code);
    return param.getOne(baseMapper.selectListRel(param));
  }

  @Override
  @Transactional(rollbackFor = {Exception.class})
  public ApiResult<?> grantCmsPermission(Integer refTenant, Integer curTenant) {
    // 1.当前租户的超级管理员角色, 必须带租户条件
    final Role superAdmin = roleService.getOne(new LambdaQueryWrapper<Role>()
      .eq(Role::getRoleCode, "superAdmin")
      .eq(Role::getTenantId, curTenant)
      .last("limit 1"));
    if (superAdmin == null) {
      return new ApiResult<>(Constants.RESULT_ERROR_CODE, "当前租户没有superAdmin角色, 无法授权");
    }
    final Integer roleId = superAdmin.getRoleId();

    // 2.跨租户读取参考租户的全部未删除菜单
    final MenuParam refParam = new MenuParam();
    refParam.setTenantId(refTenant);
    refParam.setDeleted(0);
    final List<Menu> refMenus = menuMapper.getMenuByClone(refParam);
    if (CollectionUtils.isEmpty(refMenus)) {
      return new ApiResult<>(Constants.RESULT_ERROR_CODE, "参考租户[" + refTenant + "]没有可复制的菜单");
    }
    final Map<Integer, Menu> refMenuMap = new HashMap<>();
    for (Menu menu : refMenus) {
      refMenuMap.put(menu.getMenuId(), menu);
    }

    // 3.筛选cms:*子树, 权限标识以cms:开头的节点 + 其全部祖先, 保证父级完整
    final Set<Integer> cmsMenuIds = new HashSet<>();
    for (Menu menu : refMenus) {
      if (menu.getAuthority() == null || !menu.getAuthority().startsWith(CMS_AUTHORITY_PREFIX)) {
        continue;
      }
      Menu current = menu;
      // add返回false说明该节点及其祖先已收录, 无需继续向上追溯
      while (current != null && cmsMenuIds.add(current.getMenuId())) {
        final Integer parentId = current.getParentId();
        current = (parentId == null || parentId == 0) ? null : refMenuMap.get(parentId);
      }
    }
    if (cmsMenuIds.isEmpty()) {
      return new ApiResult<>(Constants.RESULT_ERROR_CODE, "参考租户[" + refTenant + "]没有cms权限菜单");
    }

    // 4.当前租户已有菜单, 用于去重, 保证接口幂等
    final MenuParam curParam = new MenuParam();
    curParam.setTenantId(curTenant);
    curParam.setDeleted(0);
    final Map<String, Integer> curMenuKeys = new HashMap<>();
    for (Menu menu : menuMapper.getMenuByClone(curParam)) {
      curMenuKeys.putIfAbsent(getMenuKey(menu), menu.getMenuId());
    }

    // 5.按层级复制菜单, 父菜单先于子菜单插入
    final List<Menu> sources = refMenus.stream()
      .filter(d -> cmsMenuIds.contains(d.getMenuId()))
      .sorted(Comparator.comparingInt((Menu d) -> getMenuDepth(d, refMenuMap)))
      .collect(Collectors.toList());
    final Map<Integer, Integer> menuIdMapping = new HashMap<>();
    int menuCount = 0;
    for (Menu source : sources) {
      // 已存在的菜单不重复插入, 直接复用其菜单id作为子菜单的父级
      final Integer existsMenuId = curMenuKeys.get(getMenuKey(source));
      if (existsMenuId != null) {
        menuIdMapping.put(source.getMenuId(), existsMenuId);
        continue;
      }
      final Menu menu = new Menu();
      menu.setParentId(source.getParentId() == null ? 0 : menuIdMapping.getOrDefault(source.getParentId(), 0));
      menu.setTitle(source.getTitle());
      menu.setPath(source.getPath());
      menu.setComponent(source.getComponent());
      menu.setModules(source.getModules());
      menu.setModulesUrl(source.getModulesUrl());
      menu.setMenuType(source.getMenuType());
      menu.setSortNumber(source.getSortNumber());
      menu.setAuthority(source.getAuthority());
      menu.setIcon(source.getIcon());
      menu.setHide(source.getHide());
      menu.setMeta(source.getMeta());
      menu.setTenantId(curTenant);
      menuService.save(menu);
      menuIdMapping.put(source.getMenuId(), menu.getMenuId());
      curMenuKeys.putIfAbsent(getMenuKey(menu), menu.getMenuId());
      menuCount++;
    }

    // 6.绑定cms菜单到超级管理员角色, 已绑定的跳过
    final Set<Integer> boundMenuIds = roleMenuService.list(new LambdaQueryWrapper<RoleMenu>()
        .eq(RoleMenu::getRoleId, roleId))
      .stream().map(RoleMenu::getMenuId).collect(Collectors.toSet());
    final List<RoleMenu> roleMenus = new ArrayList<>();
    for (Integer menuId : menuIdMapping.values()) {
      if (menuId == null || boundMenuIds.contains(menuId)) {
        continue;
      }
      final RoleMenu roleMenu = new RoleMenu();
      roleMenu.setRoleId(roleId);
      roleMenu.setMenuId(menuId);
      roleMenu.setTenantId(curTenant);
      roleMenus.add(roleMenu);
    }
    if (!roleMenus.isEmpty()) {
      roleMenuService.saveBatch(roleMenus);
    }

    final Map<String, Object> data = new HashMap<>();
    data.put("refTenantId", refTenant);
    data.put("tenantId", curTenant);
    data.put("roleId", roleId);
    data.put("menuTotal", menuIdMapping.size());
    data.put("menuCreated", menuCount);
    data.put("roleMenuCreated", roleMenus.size());
    return new ApiResult<>(Constants.RESULT_OK_CODE, "授权成功", data);
  }

  // 菜单去重标识, 优先使用权限标识, 目录类菜单权限标识为空时按标题+路由匹配
  private String getMenuKey(Menu menu) {
    if (StrUtil.isNotBlank(menu.getAuthority())) {
      return "authority:" + menu.getAuthority();
    }
    return "menu:" + StrUtil.nullToEmpty(menu.getTitle()) + "@" + StrUtil.nullToEmpty(menu.getPath());
  }

  // 菜单层级, 用于保证父菜单先于子菜单插入
  private int getMenuDepth(Menu menu, Map<Integer, Menu> menuMap) {
    int depth = 0;
    Menu current = menu;
    while (current != null && current.getParentId() != null && current.getParentId() != 0 && depth < 20) {
      current = menuMap.get(current.getParentId());
      depth++;
    }
    return depth;
  }

}
