package com.ningdatech.carapi.sys.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ningdatech.cache.repository.CachePlusOps;
import com.ningdatech.carapi.sys.entity.RoleAuthority;
import com.ningdatech.carapi.sys.entity.UserRole;
import com.ningdatech.carapi.sys.entity.dto.RoleAuthoritySaveDTO;
import com.ningdatech.carapi.sys.entity.dto.RoleUserSaveVO;
import com.ningdatech.carapi.sys.entity.dto.UserRoleSaveDTO;
import com.ningdatech.carapi.sys.entity.enumeration.AuthorizeType;
import com.ningdatech.carapi.sys.mapper.ResourceMapper;
import com.ningdatech.carapi.sys.mapper.RoleAuthorityMapper;
import com.ningdatech.carapi.sys.service.RoleAuthorityService;
import com.ningdatech.carapi.sys.service.UserRoleService;
import com.ningdatech.carapi.sys.utils.AuthCacheKeyUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 业务实现类
 * 角色的资源
 * </p>
 *
 * @author PoffyZhang
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class RoleAuthorityServiceImpl extends ServiceImpl<RoleAuthorityMapper, RoleAuthority> implements RoleAuthorityService {

    private final UserRoleService userRoleService;
    private final ResourceMapper resourceMapper;
    private final CachePlusOps cachePlusOps;

    @Override
    public List<Long> selectDataScopeIdFromRoleByUserId(Long userId, String category) {
        return baseMapper.selectDataScopeIdFromRoleByUserId(userId, category);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<Long> saveRoleUser(RoleUserSaveVO saveVO) {
        Assert.notEmpty(saveVO.getUserIdList(), "请选择用户");
        if (saveVO.getFlag() == null) {
            saveVO.setFlag(true);
        }

        userRoleService.remove(Wrappers.lambdaQuery(UserRole.class).eq(UserRole::getRoleId, saveVO.getRoleId()).in(UserRole::getUserId, saveVO.getUserIdList()));
        if (saveVO.getFlag()) {
            List<UserRole> list = saveVO.getUserIdList().stream().map(employeeId ->
                    UserRole.builder().userId(employeeId).roleId(saveVO.getRoleId()).build()).collect(Collectors.toList());
            userRoleService.saveBatch(list);
        }
        return findUserIdByRoleId(saveVO.getRoleId());
    }

    @Override
    public List<Long> findUserIdByRoleId(Long roleId) {
        return userRoleService.listObjs(Wrappers.lambdaQuery(UserRole.class)
                        .select(UserRole::getUserId).eq(UserRole::getRoleId, roleId),
                Convert::toLong);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveUserRole(UserRoleSaveDTO userRole) {
        List<UserRole> oldUserRoleList = userRoleService.list(Wrappers.lambdaQuery(UserRole.class).eq(UserRole::getRoleId, userRole.getRoleId()));
        userRoleService.remove(Wrappers.lambdaQuery(UserRole.class).eq(UserRole::getRoleId, userRole.getRoleId()));

        Set<Long> delIdList = new HashSet<>();
        if (CollUtil.isNotEmpty(userRole.getUserIdList())) {
            List<UserRole> list = userRole.getUserIdList()
                    .stream()
                    .map(userId -> UserRole.builder()
                            .userId(userId)
                            .roleId(userRole.getRoleId())
                            .build())
                    .collect(Collectors.toList());
            userRoleService.saveBatch(list);
            delIdList.addAll(userRole.getUserIdList());
        }

        /* 角色A -> u1 u2
            修改成
            角色A -> u3 u2
            所以， 应该清除 u1、u2、u3 的角色、菜单、资源
            */

        if (!oldUserRoleList.isEmpty()) {
            delIdList.addAll(oldUserRoleList.stream().map(UserRole::getUserId).collect(Collectors.toSet()));
        }
        delUserAuthority(delIdList);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveRoleAuthority(RoleAuthoritySaveDTO dto) {
        Assert.notNull(dto.getRoleId(), "请选择角色");

        //删除角色和资源的关联
        super.remove(Wrappers.lambdaQuery(RoleAuthority.class).eq(RoleAuthority::getRoleId, dto.getRoleId()));

        List<RoleAuthority> list = new ArrayList<>();
        if (dto.getResourceIdList() != null && !dto.getResourceIdList().isEmpty()) {
            List<Long> menuIdList = resourceMapper.findMenuIdByResourceId(dto.getResourceIdList());
            if (dto.getMenuIdList() == null || dto.getMenuIdList().isEmpty()) {
                dto.setMenuIdList(menuIdList);
            } else {
                dto.getMenuIdList().addAll(menuIdList);
            }

            //保存授予的资源resourceMapper
            List<RoleAuthority> resourceList = new HashSet<>(dto.getResourceIdList())
                    .stream()
                    .map(resourceId -> RoleAuthority.builder()
                            .authorityType(AuthorizeType.RESOURCE)
                            .authorityId(resourceId)
                            .roleId(dto.getRoleId())
                            .build())
                    .collect(Collectors.toList());
            list.addAll(resourceList);
        }
        if (dto.getMenuIdList() != null && !dto.getMenuIdList().isEmpty()) {
            //保存授予的菜单
            List<RoleAuthority> menuList = new HashSet<>(dto.getMenuIdList())
                    .stream()
                    .map(menuId -> RoleAuthority.builder()
                            .authorityType(AuthorizeType.MENU)
                            .authorityId(menuId)
                            .roleId(dto.getRoleId())
                            .build())
                    .collect(Collectors.toList());
            list.addAll(menuList);
        }
        super.saveBatch(list);

        // 角色下的所有用户
        List<Long> userIdList = userRoleService.listObjs(
                Wrappers.lambdaQuery(UserRole.class)
                        .select(UserRole::getUserId)
                        .eq(UserRole::getRoleId, dto.getRoleId()),
                Convert::toLong);

        delUserAuthority(userIdList);
        cachePlusOps.del(AuthCacheKeyUtils.roleResourceCacheKey(dto.getRoleId()));
        cachePlusOps.del(AuthCacheKeyUtils.roleResourceCacheKey((dto.getRoleId())));
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeByAuthorityId(List<Long> ids) {
        List<Long> roleIds = listObjs(
                Wrappers.lambdaQuery(RoleAuthority.class)
                        .select(RoleAuthority::getRoleId)
                        .in(RoleAuthority::getAuthorityId, ids),
                Convert::toLong);

        if (!roleIds.isEmpty()) {
            remove(Wrappers.lambdaQuery(RoleAuthority.class).in(RoleAuthority::getAuthorityId, ids));
            List<Long> userIdList = userRoleService.listObjs(
                    Wrappers.lambdaQuery(UserRole.class).select(UserRole::getUserId).in(UserRole::getRoleId, roleIds),
                    Convert::toLong);

            delUserAuthority(userIdList);
            roleIds.forEach(roleId -> {
                cachePlusOps.del(AuthCacheKeyUtils.roleResourceCacheKey(roleId));
                cachePlusOps.del(AuthCacheKeyUtils.roleMenuCacheKey(roleId));
            });
        }
        return true;
    }

    private void delUserAuthority(Collection<Long> collections) {
        collections.forEach(userId -> {
            cachePlusOps.del(AuthCacheKeyUtils.userResourceCacheKey(userId));
            cachePlusOps.del(AuthCacheKeyUtils.userMenuCacheKey(userId));
            cachePlusOps.del(AuthCacheKeyUtils.userRoleCacheKey(userId));
        });
    }
}
