/**
 * BladeX Commercial License Agreement
 * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
 * <p>
 * Use of this software is governed by the Commercial License Agreement
 * obtained after purchasing a license from BladeX.
 * <p>
 * 1. This software is for development use only under a valid license
 * from BladeX.
 * <p>
 * 2. Redistribution of this software's source code to any third party
 * without a commercial license is strictly prohibited.
 * <p>
 * 3. Licensees may copyright their own code but cannot use segments
 * from this software for such purposes. Copyright of this software
 * remains with BladeX.
 * <p>
 * Using this software signifies agreement to this License, and the software
 * must not be used for illegal purposes.
 * <p>
 * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
 * not liable for any claims arising from secondary or illegal development.
 * <p>
 * Author: Chill Zhuang (bladejava@qq.com)
 */
package org.springblade.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.AllArgsConstructor;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tenant.TenantGuard;
import org.springblade.core.tool.constant.BladeConstant;
import org.springblade.core.tool.constant.RoleConstant;
import org.springblade.core.tool.node.ForestNodeMerger;
import org.springblade.core.tool.utils.CollectionUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.system.pojo.entity.Role;
import org.springblade.system.pojo.entity.RoleMenu;
import org.springblade.system.pojo.entity.RoleScope;
import org.springblade.system.mapper.RoleMapper;
import org.springblade.system.service.IRoleMenuService;
import org.springblade.system.service.IRoleScopeService;
import org.springblade.system.service.IRoleService;
import org.springblade.system.pojo.vo.RoleVO;
import org.springblade.system.wrapper.RoleWrapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import jakarta.validation.constraints.NotEmpty;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static org.springblade.common.constant.CommonConstant.API_SCOPE_CATEGORY;
import static org.springblade.common.constant.CommonConstant.DATA_SCOPE_CATEGORY;
import static org.springblade.core.tenant.TenantGuard.EntityType.ROLE;
import static org.springblade.core.tenant.TenantGuard.EntityType.ROLE_PARENT;

/**
 * 服务实现类
 *
 * @author Chill
 */
@Service
@Validated
@AllArgsConstructor
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements IRoleService {

	private final IRoleMenuService roleMenuService;
	private final IRoleScopeService roleScopeService;

	@Override
	public Role getDetail(Role role) {
		QueryWrapper<Role> queryWrapper = Condition.getQueryWrapper(role);
		applyTenantScope(queryWrapper);
		return getOne(queryWrapper);
	}

	@Override
	public List<RoleVO> selectList(Map<String, Object> role) {
		QueryWrapper<Role> queryWrapper = Condition.getQueryWrapper(role, Role.class);
		applyTenantScope(queryWrapper);
		return RoleWrapper.build().listNodeVO(list(queryWrapper));
	}

	@Override
	public IPage<RoleVO> selectRolePage(IPage<RoleVO> page, RoleVO role) {
		return page.setRecords(baseMapper.selectRolePage(page, role));
	}

	@Override
	public List<RoleVO> tree(String tenantId) {
		String resolvedTenantId = AuthUtil.isAdministrator()
			? Func.toStrWithEmpty(tenantId, AuthUtil.getTenantId())
			: AuthUtil.getTenantId();
		return doTree(resolvedTenantId);
	}

	@Override
	public List<RoleVO> treeById(Long roleId) {
		String resolvedTenantId;
		if (AuthUtil.isAdministrator()) {
			Role role = getById(roleId);
			resolvedTenantId = Func.notNull(role) ? role.getTenantId() : AuthUtil.getTenantId();
		} else {
			resolvedTenantId = AuthUtil.getTenantId();
		}
		return doTree(resolvedTenantId);
	}

	/**
	 * 树形构建复用方法：根据当前用户角色决定是否需要排除内置 admin 角色，
	 * 然后委派给底层 mapper。
	 */
	private List<RoleVO> doTree(String tenantId) {
		String userRole = AuthUtil.getUserRole();
		String excludeRole = null;
		if (!CollectionUtil.contains(Func.toStrArray(userRole), RoleConstant.ADMIN) && !CollectionUtil.contains(Func.toStrArray(userRole), RoleConstant.ADMINISTRATOR)) {
			excludeRole = RoleConstant.ADMINISTRATOR;
		}
		return ForestNodeMerger.merge(baseMapper.tree(tenantId, excludeRole));
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean grant(@NotEmpty List<Long> roleIds, List<Long> menuIds, List<Long> dataScopeIds, List<Long> apiScopeIds) {
		// 租户守卫：批量校验目标角色全部归属当前会话租户
		TenantGuard.verifyBatch(this, roleIds, ROLE);
		return grantRoleMenu(roleIds, menuIds) && grantDataScope(roleIds, dataScopeIds) && grantApiScope(roleIds, apiScopeIds);
	}

	private boolean grantRoleMenu(List<Long> roleIds, List<Long> menuIds) {
		// 防止越权配置超管角色
		Long administratorCount = baseMapper.selectCount(Wrappers.<Role>query().lambda().eq(Role::getRoleAlias, RoleConstant.ADMINISTRATOR).in(Role::getId, roleIds));
		if (!AuthUtil.isAdministrator() && administratorCount > 0L) {
			throw new ServiceException("无权配置超管角色!");
		}
		// 防止越权配置管理员角色
		Long adminCount = baseMapper.selectCount(Wrappers.<Role>query().lambda().eq(Role::getRoleAlias, RoleConstant.ADMIN).in(Role::getId, roleIds));
		if (!AuthUtil.isAdmin() && adminCount > 0L) {
			throw new ServiceException("无权配置管理员角色!");
		}
		// 删除角色配置的菜单集合
		roleMenuService.remove(Wrappers.<RoleMenu>update().lambda().in(RoleMenu::getRoleId, roleIds));
		// 组装配置
		List<RoleMenu> roleMenus = new ArrayList<>();
		roleIds.forEach(roleId -> menuIds.forEach(menuId -> {
			RoleMenu roleMenu = new RoleMenu();
			roleMenu.setRoleId(roleId);
			roleMenu.setMenuId(menuId);
			roleMenus.add(roleMenu);
		}));
		// 新增配置
		roleMenuService.saveBatch(roleMenus);
		// 递归设置下属角色菜单集合
		recursionRoleMenu(roleIds, menuIds);
		return true;
	}

	private void recursionRoleMenu(List<Long> roleIds, List<Long> menuIds) {
		roleIds.forEach(roleId -> baseMapper.selectList(Wrappers.<Role>query().lambda().eq(Role::getParentId, roleId)).forEach(role -> {
			List<RoleMenu> roleMenuList = roleMenuService.list(Wrappers.<RoleMenu>query().lambda().eq(RoleMenu::getRoleId, role.getId()));
			// 子节点过滤出父节点删除的菜单集合
			List<Long> collectRoleMenuIds = roleMenuList.stream().map(RoleMenu::getMenuId).filter(menuId -> !menuIds.contains(menuId)).collect(Collectors.toList());
			if (collectRoleMenuIds.size() > 0) {
				// 删除子节点权限外的菜单集合
				roleMenuService.remove(Wrappers.<RoleMenu>update().lambda().eq(RoleMenu::getRoleId, role.getId()).in(RoleMenu::getMenuId, collectRoleMenuIds));
				// 递归设置下属角色菜单集合
				recursionRoleMenu(Collections.singletonList(role.getId()), menuIds);
			}
		}));
	}

	private boolean grantDataScope(List<Long> roleIds, List<Long> dataScopeIds) {
		// 删除角色配置的数据权限集合
		roleScopeService.remove(Wrappers.<RoleScope>update().lambda().eq(RoleScope::getScopeCategory, DATA_SCOPE_CATEGORY).in(RoleScope::getRoleId, roleIds));
		// 组装配置
		List<RoleScope> roleDataScopes = new ArrayList<>();
		roleIds.forEach(roleId -> dataScopeIds.forEach(scopeId -> {
			RoleScope roleScope = new RoleScope();
			roleScope.setScopeCategory(DATA_SCOPE_CATEGORY);
			roleScope.setRoleId(roleId);
			roleScope.setScopeId(scopeId);
			roleDataScopes.add(roleScope);
		}));
		// 新增配置
		roleScopeService.saveBatch(roleDataScopes);
		return true;
	}

	private boolean grantApiScope(List<Long> roleIds, List<Long> apiScopeIds) {
		// 删除角色配置的接口权限集合
		roleScopeService.remove(Wrappers.<RoleScope>update().lambda().eq(RoleScope::getScopeCategory, API_SCOPE_CATEGORY).in(RoleScope::getRoleId, roleIds));
		// 组装配置
		List<RoleScope> roleApiScopes = new ArrayList<>();
		roleIds.forEach(roleId -> apiScopeIds.forEach(scopeId -> {
			RoleScope roleScope = new RoleScope();
			roleScope.setScopeCategory(API_SCOPE_CATEGORY);
			roleScope.setScopeId(scopeId);
			roleScope.setRoleId(roleId);
			roleApiScopes.add(roleScope);
		}));
		// 新增配置
		roleScopeService.saveBatch(roleApiScopes);
		return true;
	}

	@Override
	public String getRoleIds(String tenantId, String roleNames) {
		List<Role> roleList = baseMapper.selectList(Wrappers.<Role>query().lambda().eq(Role::getTenantId, tenantId).in(Role::getRoleName, Func.toStrList(roleNames)));
		if (roleList != null && roleList.size() > 0) {
			return roleList.stream().map(role -> Func.toStr(role.getId())).distinct().collect(Collectors.joining(","));
		}
		return null;
	}

	@Override
	public List<String> getRoleNames(String roleIds) {
		return baseMapper.getRoleNames(Func.toLongArray(roleIds));
	}

	@Override
	public List<String> getRoleAliases(String roleIds) {
		return baseMapper.getRoleAliases(Func.toLongArray(roleIds));
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean submit(Role role) {
		if (!AuthUtil.isAdministrator()) {
			if (Func.toStr(role.getRoleAlias()).equals(RoleConstant.ADMINISTRATOR)) {
				throw new ServiceException("无权限创建超管角色！");
			}
		}
		Long parentId = role.getParentId();
		// 顶级语义：parentId 为 null 或 TOP_PARENT_ID (0L) 均视为顶级角色
		if (parentId == null || parentId.equals(BladeConstant.TOP_PARENT_ID)) {
			// 顶级角色：租户守卫统一处理新增 / 修改路径的租户绑定
			TenantGuard.bindTenant(this, role, ROLE);
			role.setParentId(BladeConstant.TOP_PARENT_ID);
		} else {
			// 子角色：先校验自身归属（修改路径），再校验父节点归属并继承其 tenantId
			if (parentId.equals(role.getId())) {
				throw new ServiceException("父节点不可选择自身!");
			}
			Role self = Func.isNotEmpty(role.getId())
				? TenantGuard.verify(this, role.getId(), ROLE) : null;
			Role parent = TenantGuard.verify(this, role.getParentId(), ROLE_PARENT);
			if (parent == null) {
				throw new ServiceException("上级角色不存在!");
			}
			// 显式禁止跨租户迁移：保护现有子树的数据一致性，self 租户为空时放行并继承父租户
			if (self != null && Func.isNotEmpty(self.getTenantId()) && !self.getTenantId().equals(parent.getTenantId())) {
				throw new ServiceException("不允许跨租户迁移角色，请联系运维处理");
			}
			role.setTenantId(parent.getTenantId());
		}
		role.setIsDeleted(BladeConstant.DB_NOT_DELETED);
		if (Func.isEmpty(role.getTenantId())) {
			if (AuthUtil.isAdministrator()) {
				role.setTenantId(BladeConstant.ADMIN_TENANT_ID);
			} else {
				throw new ServiceException("会话租户不可识别，拒绝创建角色");
			}
		}
		return saveOrUpdate(role);
	}

	@Override
	public List<RoleVO> search(String roleName, Long parentId) {
		String tenantId = AuthUtil.getTenantId();
		LambdaQueryWrapper<Role> queryWrapper = Wrappers.<Role>query().lambda();
		if (Func.isNotEmpty(roleName)) {
			queryWrapper.like(Role::getRoleName, roleName);
		}
		if (Func.isNotEmpty(parentId) && parentId > 0L) {
			queryWrapper.eq(Role::getParentId, parentId);
		}
		if (Func.isNotEmpty(tenantId)) {
			queryWrapper.eq(Role::getTenantId, tenantId);
		}
		List<Role> roleList = baseMapper.selectList(queryWrapper);
		return RoleWrapper.build().listNodeVO(roleList);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean removeRole(String ids) {
		List<Long> idList = Func.toLongList(ids);
		// 租户守卫：批量校验目标角色全部归属当前会话租户
		TenantGuard.verifyBatch(this, idList, ROLE);
		Long cnt = baseMapper.selectCount(Wrappers.<Role>query().lambda().in(Role::getParentId, idList));
		if (cnt > 0L) {
			throw new ServiceException("请先删除子节点!");
		}
		return removeByIds(idList);
	}

	@Override
	public List<Role> alias(String tenantId) {
		// 获取所有角色数据
		List<Role> roles = baseMapper.selectList(Wrappers.<Role>lambdaQuery().eq(Role::getTenantId, tenantId));

		// 根据 roleAlias 对角色进行分组
		Map<String, List<String>> aliasToNamesMap = roles.stream()
			.collect(Collectors.groupingBy(Role::getRoleAlias,
				Collectors.mapping(Role::getRoleName, Collectors.toList())));

		// 创建新的角色列表，每个角色的 roleName 是 roleAlias 后跟括号内的所有 roleName
		return aliasToNamesMap.entrySet().stream()
			// 过滤掉超级管理员角色
			.filter(entry -> !StringUtil.equals(RoleConstant.ADMINISTRATOR, entry.getKey()))
			.map(entry -> {
				String roleAlias = entry.getKey();
				List<String> names = entry.getValue();
				String namesConcat = names.stream().distinct().collect(Collectors.joining(StringPool.COMMA + StringPool.SPACE));
				Role role = new Role();
				role.setRoleAlias(roleAlias);
				role.setRoleName(roleAlias + StringPool.SPACE + StringPool.LEFT_BRACKET + namesConcat + StringPool.RIGHT_BRACKET);
				return role;
			}).collect(Collectors.toList());
	}

	/**
	 * 统一的租户范围过滤：超管放行，其他用户强制限定为当前会话租户。
	 */
	private void applyTenantScope(QueryWrapper<Role> queryWrapper) {
		if (!AuthUtil.isAdministrator()) {
			queryWrapper.lambda().eq(Role::getTenantId, AuthUtil.getTenantId());
		}
	}

}
