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

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpStatus;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ningdatech.basic.exception.BaseUncheckedException;
import com.ningdatech.basic.exception.BizException;
import com.ningdatech.basic.exception.code.ExceptionCode;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.cache.repository.CachePlusOps;
import com.ningdatech.pmapi.sys.model.dto.MenuDataScopeDTO;
import com.ningdatech.pmapi.sys.model.dto.RoleSaveDTO;
import com.ningdatech.pmapi.sys.model.dto.RoleUpdateDTO;
import com.ningdatech.pmapi.sys.mapper.RoleMapper;
import com.ningdatech.pmapi.sys.model.entity.*;
import com.ningdatech.pmapi.sys.service.*;
import com.ningdatech.pmapi.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 RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements IRoleService {

    private final IUserRoleService IUserRoleService;
    private final RoleMapper roleMapper;
    private final IRoleMenuDatascopeService roleMenuDatascopeService;

    private final CachePlusOps cachePlusOps;

    private final IRoleMenuService roleMenuService;

    private final IMenuService IMenuService;

    private static final String SUPER_ADMIN = "SUPER_ADMIN";


    /**
     * 删除角色时，需要级联删除跟角色相关的一切资源：
     * 1，角色本身
     * 2，角色-组织：
     * 3，角色-权限（菜单和按钮）：
     * 4，角色-用户：角色拥有的用户
     * 5，用户-权限：
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeByIdWithCache(List<Long> ids) {
        if (ids.isEmpty()) {
            return true;
        }

        if (IUserRoleService.count(Wrappers.lambdaQuery(UserRole.class).select(UserRole::getUserId)
                .in(UserRole::getRoleId, ids)) > 0) {
            throw new BizException(ExceptionCode.OPERATION_EX.getCode(), "该角色下还有用户 不能随意删除!");
        }

        // 角色
        boolean removeFlag = removeByIds(ids);

        //角色拥有的用户
        IUserRoleService.remove(Wrappers.lambdaQuery(UserRole.class).in(UserRole::getRoleId, ids));
        if (removeFlag) {
            // 角色绑定了那些用户
            if (roleMenuService.remove(Wrappers.lambdaQuery(RoleMenu.class).in(RoleMenu::getRoleId, ids))) {
                return Boolean.TRUE;
            }
        }
        return removeFlag;
    }

    /**
     * 1、根据 USER_ROLE:{userId} 查询用户拥有的角色ID集合
     * 2、缓存中有，则根据角色ID集合查询 角色集合
     * 3、缓存中有查不到，则从DB查询，并写入缓存， 立即返回
     *
     * @param userId 用户id
     */
    @Override
    public List<Role> findRoleByUserId(Long userId) {
        String cacheKey = AuthCacheKeyUtils.userRoleCacheKey(userId);
        List<Role> roleList = new ArrayList<>();
        List<Long> list = cachePlusOps.get(cacheKey);
        if (list == null) {
            roleList.addAll(baseMapper.findRoleByUserId(userId));
            list = roleList.stream().map(Role::getId).collect(Collectors.toList());
        }
        if (roleList.isEmpty()) {
            return listByIds(list);
        }
        return roleList;
    }

    /**
     * 1，保存角色
     * 2，保存 与组织的关系
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveRole(RoleSaveDTO data, Long userId) {
        if (StrUtil.isNotBlank(data.getName()) && check(data.getName())) {
            throw new BaseUncheckedException(HttpStatus.HTTP_OK, "角色名称{} 已存在", data.getName());
        }
        Role role = BeanUtil.toBean(data, Role.class);
        role.setCreateBy(userId);
        role.setUpdateBy(userId);
        save(role);
        saveRoleMenu(data.getMenuIds(), role.getId());
        saveRoleMenuDatascope(data.getMenuDataScopeList(), role.getId());
    }

    private void saveRoleMenuDatascope(List<MenuDataScopeDTO> menuDataScopeList, Long roleId) {
        LambdaQueryWrapper<RoleMenuDatascope> query = Wrappers.lambdaQuery(RoleMenuDatascope.class)
                .eq(RoleMenuDatascope::getRoleId, roleId);
        roleMenuDatascopeService.remove(query);
        if (menuDataScopeList == null || menuDataScopeList.isEmpty()) {
            return;
        }
        List<RoleMenuDatascope> datascopeList = CollUtils.convert(menuDataScopeList, w -> {
            RoleMenuDatascope roleMenuDatascope = new RoleMenuDatascope();
            roleMenuDatascope.setMenuId(w.getMenuId());
            roleMenuDatascope.setDatascope(w.getDataScope());
            roleMenuDatascope.setRoleId(roleId);
            return roleMenuDatascope;
        });
        roleMenuDatascopeService.saveBatch(datascopeList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateRole(RoleUpdateDTO data, Long userId) {
        Role roleOld = roleMapper.selectById(data.getId());
        if (Objects.isNull(roleOld)) {
            throw new BaseUncheckedException(HttpStatus.HTTP_OK, String.format("修改用户%d 为空", data.getId()));
        }

        if (StrUtil.isNotBlank(data.getName()) && check(data.getName(), roleOld.getId())) {
            throw BizException.wrap("角色名{} 已经存在", data.getName());
        }
        Role role = BeanUtil.toBean(data, Role.class);
        role.setUpdateBy(userId);
        updateById(role);
        saveRoleMenu(data.getMenuIds(), role.getId());
        saveRoleMenuDatascope(data.getMenuDataScopeList(), role.getId());
        cachePlusOps.del(AuthCacheKeyUtils.userResourceCacheKey(userId));
    }

    @Override
    public List<Long> findUserIdByCode(String[] codes) {
        return baseMapper.findUserIdByCode(codes);
    }

    @Override
    public Boolean check(String name) {
        return super.count(Wrappers.lambdaQuery(Role.class).eq(Role::getName, name)) > 0;
    }

    @Override
    public Boolean check(String name, Long id) {
        return super.count(Wrappers.lambdaQuery(Role.class).ne(Role::getId, id)
                .eq(Role::getName, name)) > 0;
    }

    private void saveRoleMenu(List<Long> menuIds, Long roleId) {
        if (CollUtil.isNotEmpty(menuIds)) {
            roleMenuService.remove(Wrappers.lambdaQuery(RoleMenu.class).eq(RoleMenu::getRoleId, roleId));
            Set<RoleMenu> toAddMenus = new HashSet<>();
            for (Long menuId : menuIds) {
                Menu menu = IMenuService.getById(menuId);
                if (Objects.isNull(menu)) {
                    continue;
                }
                RoleMenu roleMenu = new RoleMenu(null, menuId, roleId);
                toAddMenus.add(roleMenu);
                // 父级菜单
                // addParent(toAddMenus,menu,roleId);
            }
            if (CollUtil.isNotEmpty(toAddMenus)) {
                roleMenuService.saveBatch(toAddMenus);
            }
        }
    }

    private void addParent(Set<RoleMenu> toAddMenus, Menu menu, Long roleId) {
        if (Objects.isNull(menu.getPid()) || menu.getPid().equals(0L)) {
            return;
        }
        Menu parent = IMenuService.getById(menu.getPid());
        if (Objects.isNull(parent)) {
            return;
        }
        RoleMenu roleMenu = new RoleMenu(null, parent.getId(), roleId);
        toAddMenus.add(roleMenu);
        addParent(toAddMenus, parent, roleId);
    }
}
