package com.xunhong.erp.turbo.rbac.infrastructure.gateway;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.cola.exception.BizException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xunhong.erp.turbo.api.rbac.dto.cmd.RoleCreateCmd;
import com.xunhong.erp.turbo.api.rbac.dto.cmd.RoleDestroyCmd;
import com.xunhong.erp.turbo.api.rbac.dto.cmd.RoleUpdateCmd;
import com.xunhong.erp.turbo.api.rbac.dto.qry.RoleListQry;
import com.xunhong.erp.turbo.api.rbac.dto.qry.RolePageQry;
import com.xunhong.erp.turbo.api.rbac.dto.qry.RoleShowQry;
import com.xunhong.erp.turbo.rbac.domain.entity.Role;
import com.xunhong.erp.turbo.rbac.domain.gateway.RoleGateway;
import com.xunhong.erp.turbo.rbac.infrastructure.convert.RoleConvert;
import com.xunhong.erp.turbo.rbac.infrastructure.entity.*;
import com.xunhong.erp.turbo.rbac.infrastructure.mapper.*;
import lombok.RequiredArgsConstructor;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;

@Repository
@RequiredArgsConstructor
public class RoleGatewayImpl implements RoleGateway {
	private final RoleMenuMapper roleMenuMapper;
	private final RoleMapper roleMapper;
	private final RoleConvert roleConvert;
	private final SqlSessionFactory sqlSessionFactory;
	private final RolePermissionMapper rolePermissionMapper;
	private final PermissionMapper permissionMapper;
	private final MenuMapper menuMapper;
	private final UserRoleMapper userRoleMapper;

	@Override
	public List<Role> list(RoleListQry roleListQry) {
		LambdaQueryWrapper<RoleDO> queryWrapper = Wrappers.lambdaQuery(RoleDO.class);
		queryWrapper.eq(CharSequenceUtil.isNotBlank(roleListQry.getName()), RoleDO::getName, roleListQry.getName());
		queryWrapper.eq(Objects.nonNull(roleListQry.getType()), RoleDO::getType, roleListQry.getType());
		queryWrapper.eq(Objects.nonNull(roleListQry.getRoleId()), RoleDO::getRoleId, roleListQry.getRoleId());
		queryWrapper.eq(Objects.nonNull(roleListQry.getPlatformId()), RoleDO::getPlatformId, roleListQry.getPlatformId());
		queryWrapper.in(CollUtil.isNotEmpty(roleListQry.getPlatformIdList()), RoleDO::getPlatformId, roleListQry.getPlatformIdList());
		queryWrapper.in(CollUtil.isNotEmpty(roleListQry.getRoleIdList()), RoleDO::getRoleId, roleListQry.getRoleIdList());

		List<RoleDO> roleDOList = roleMapper.selectList(queryWrapper);
		return roleDOList.stream().map(roleConvert::toRole).toList();
	}

	@Override
	@Transactional
	public Role save(RoleCreateCmd roleCreateCmd) {
		// 创建角色
		RoleDO roleDO = roleConvert.toRoleDO(roleCreateCmd);
		roleMapper.insert(roleDO);

		SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
		RolePermissionMapper rolePermissionMapper1 = sqlSession.getMapper(RolePermissionMapper.class);
		RoleMenuMapper roleMenuMapper1 = sqlSession.getMapper(RoleMenuMapper.class);

		// 根据提供的菜单查询有效菜单
		LambdaQueryWrapper<MenuDO> queryWrapper = Wrappers.lambdaQuery(MenuDO.class);
		queryWrapper.in(MenuDO::getMenuId, roleCreateCmd.getMenuId());
		List<MenuDO> menuDOList = menuMapper.selectList(queryWrapper);

		// 角色关联菜单
		menuDOList.forEach(menuDO -> {
			RoleMenuDO roleMenuDO = new RoleMenuDO();
			roleMenuDO.setMenuId(menuDO.getMenuId());
			roleMenuDO.setRoleId(roleDO.getRoleId());
			roleMenuMapper1.insert(roleMenuDO);
		});

		// 角色关联权限
		List<Long> permissionIds = menuDOList.stream().map(MenuDO::getPermissionId).toList();
		List<PermissionDO> permissionDOList = permissionMapper.selectBatchIds(permissionIds);
		permissionDOList.forEach(permissionDO -> {
			RolePermissionDO rolePermissionDO = new RolePermissionDO();
			rolePermissionDO.setPermissionId(permissionDO.getPermissionId());
			rolePermissionDO.setRoleId(roleDO.getRoleId());
			rolePermissionMapper1.insert(rolePermissionDO);
		});

		sqlSession.commit();
		sqlSession.clearCache();

		return roleConvert.toRole(roleDO);
	}

	@Override
	public void destroy(RoleDestroyCmd roleDeleteCmd) {
		// 判断角色是否存在
		RoleDO roleDO = roleMapper.selectById(roleDeleteCmd.getRoleId());
		if (Objects.isNull(roleDO)) {
			throw new BizException("400", "角色不存在，无法删除");
		}

		// 判断角色是否被使用
		LambdaQueryWrapper<UserRoleDO> queryWrapper = Wrappers.lambdaQuery(UserRoleDO.class);
		queryWrapper.eq(UserRoleDO::getRoleId, roleDeleteCmd.getRoleId());
		if (userRoleMapper.selectCount(queryWrapper) > 0) {
			throw new BizException("400", "角色被使用，无法删除");
		}

		// 删除角色关联菜单
		roleMenuMapper.delete(Wrappers.lambdaQuery(RoleMenuDO.class).eq(RoleMenuDO::getRoleId, roleDeleteCmd.getRoleId()));

		// 删除角色关联权限
		rolePermissionMapper.delete(Wrappers.lambdaQuery(RolePermissionDO.class).eq(RolePermissionDO::getRoleId, roleDeleteCmd.getRoleId()));

		// 删除角色
		roleMapper.deleteById(roleDeleteCmd.getRoleId());
	}

	@Override
	@Transactional
	public Role update(RoleUpdateCmd roleUpdateCmd) {
		// 更新角色
		RoleDO roleDO = roleConvert.toRoleDO(roleUpdateCmd);
		roleMapper.updateById(roleDO);

		SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
		RolePermissionMapper rolePermissionMapper1 = sqlSession.getMapper(RolePermissionMapper.class);
		RoleMenuMapper roleMenuMapper1 = sqlSession.getMapper(RoleMenuMapper.class);

		// 删除角色权限关联
		rolePermissionMapper1.delete(Wrappers.lambdaQuery(RolePermissionDO.class).eq(RolePermissionDO::getRoleId, roleDO.getRoleId()));
		// 删除角色菜单关联
		roleMenuMapper1.delete(Wrappers.lambdaQuery(RoleMenuDO.class).eq(RoleMenuDO::getRoleId, roleDO.getRoleId()));

		// 角色关联菜单
		LambdaQueryWrapper<MenuDO> queryWrapper = Wrappers.lambdaQuery(MenuDO.class);
		queryWrapper.in(MenuDO::getMenuId, roleUpdateCmd.getMenuId());
		List<MenuDO> menuDOList = menuMapper.selectList(queryWrapper);
		menuDOList.forEach(menuDO -> {
			RoleMenuDO roleMenuDO = new RoleMenuDO();
			roleMenuDO.setMenuId(menuDO.getMenuId());
			roleMenuDO.setRoleId(roleDO.getRoleId());
			roleMenuMapper1.insert(roleMenuDO);
		});

		// 角色关联权限
		List<Long> permissionIds = menuDOList.stream().map(MenuDO::getPermissionId).toList();
		List<PermissionDO> permissionDOList = permissionMapper.selectBatchIds(permissionIds);
		permissionDOList.forEach(permissionDO -> {
			RolePermissionDO rolePermissionDO = new RolePermissionDO();
			rolePermissionDO.setPermissionId(permissionDO.getPermissionId());
			rolePermissionDO.setRoleId(roleDO.getRoleId());
			rolePermissionMapper1.insert(rolePermissionDO);
		});

		sqlSession.commit();
		sqlSession.clearCache();

		return roleConvert.toRole(roleDO);
	}

	@Override
	public IPage<Role> page(RolePageQry rolePageQry) {
		LambdaQueryWrapper<RoleDO> queryWrapper = Wrappers.lambdaQuery(RoleDO.class);
		queryWrapper.like(StrUtil.isNotEmpty(rolePageQry.getName()), RoleDO::getName, rolePageQry.getName());
		queryWrapper.eq(Objects.nonNull(rolePageQry.getRoleId()), RoleDO::getRoleId, rolePageQry.getRoleId());
		queryWrapper.eq(Objects.nonNull(rolePageQry.getType()), RoleDO::getType, rolePageQry.getType());
		queryWrapper.eq(Objects.nonNull(rolePageQry.getSlug()), RoleDO::getSlug, rolePageQry.getSlug());
		queryWrapper.eq(Objects.nonNull(rolePageQry.getPlatformId()), RoleDO::getPlatformId, rolePageQry.getPlatformId());

		Page<RoleDO> page = new Page<>(rolePageQry.getPageIndex(), rolePageQry.getPageSize());
		page = roleMapper.selectPage(page, queryWrapper);

		return page.convert(roleConvert::toRole);
	}

	@Override
	public Role show(RoleShowQry roleShowQry) {
		RoleDO roleDO = roleMapper.selectById(roleShowQry.getRoleId());
		List<RolePermissionDO> rolePermissionDOList = rolePermissionMapper.selectList(Wrappers.lambdaQuery(RolePermissionDO.class).eq(RolePermissionDO::getRoleId, roleDO.getRoleId()));
		roleDO.setRolePermissionDOList(rolePermissionDOList);
		List<RoleMenuDO> roleMenuDOList = roleMenuMapper.selectList(Wrappers.lambdaQuery(RoleMenuDO.class).eq(RoleMenuDO::getRoleId, roleDO.getRoleId()));
		roleDO.setRoleMenuDOList(roleMenuDOList);

		return roleConvert.toRole(roleDO);
	}
}
