package cn.datax.service.system.service.impl;

import cn.datax.common.base.DataScope;
import cn.datax.common.exception.DataException;
import cn.datax.service.system.api.dto.UserDto;
import cn.datax.service.system.api.dto.UserPasswordDto;
import cn.datax.service.system.api.entity.*;
import cn.datax.service.system.api.vo.RoleVo;
import cn.datax.service.system.api.vo.UserInfo;
import cn.datax.service.system.api.vo.UserVo;
import cn.datax.service.system.dao.*;
import cn.datax.service.system.mapstruct.UserMapper;
import cn.datax.service.system.service.UserService;
import cn.datax.common.base.BaseServiceImpl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author yuwei
 * @since 2019-09-04
 */
@CacheConfig(cacheNames = {"users"})
@Service
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class UserServiceImpl extends BaseServiceImpl<UserDao, UserEntity> implements UserService {

    @Autowired
    private UserDao userDao;
    @Autowired
    private UserDeptDao userDeptDao;
    @Autowired
    private UserPostDao userPostDao;
    @Autowired
    private UserRoleDao userRoleDao;
    @Autowired
    private MenuDao menuDao;
    @Autowired
    private UserMapper userMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveUser(UserDto userDto) {
        UserEntity user = userMapper.toEntity(userDto);
        int n = userDao.selectCount(Wrappers.<UserEntity>lambdaQuery().eq(UserEntity::getUsername, user.getUsername()));
        if(n > 0){
            throw new DataException("该用户名已存在");
        }
        String passwordEncode = new BCryptPasswordEncoder().encode(user.getPassword());
        user.setPassword(passwordEncode);
        userDao.insert(user);
        if(CollUtil.isNotEmpty(userDto.getRoleList())){
            insertBatchRole(userDto.getRoleList(), user.getId());
        }
        if(CollUtil.isNotEmpty(userDto.getDeptList())){
            insertBatchDept(userDto.getDeptList(), user.getId());
        }
        if(CollUtil.isNotEmpty(userDto.getPostList())){
            insertBatchPost(userDto.getPostList(), user.getId());
        }
    }

    private void insertBatchPost(List<String> posts, String userId) {
        List<UserPostEntity> userPostList = posts
                .stream().map(postId -> {
                    UserPostEntity userPost = new UserPostEntity();
                    userPost.setUserId(userId);
                    userPost.setPostId(postId);
                    return userPost;
                }).collect(Collectors.toList());
        userPostDao.insertBatch(userPostList);
    }

    private void insertBatchDept(List<String> deptss, String userId) {
        List<UserDeptEntity> userDeptList = deptss
                .stream().map(deptId -> {
                    UserDeptEntity userDept = new UserDeptEntity();
                    userDept.setUserId(userId);
                    userDept.setDeptId(deptId);
                    return userDept;
                }).collect(Collectors.toList());
        userDeptDao.insertBatch(userDeptList);
    }

    private void insertBatchRole(List<String> roles, String userId) {
        List<UserRoleEntity> userRoleList = roles
                .stream().map(roleId -> {
                    UserRoleEntity userRole = new UserRoleEntity();
                    userRole.setUserId(userId);
                    userRole.setRoleId(roleId);
                    return userRole;
                }).collect(Collectors.toList());
        userRoleDao.insertBatch(userRoleList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUser(UserDto userDto) {
        UserEntity user = userMapper.toEntity(userDto);
        userDao.updateById(user);
        userRoleDao.delete(Wrappers.<UserRoleEntity>lambdaQuery()
                .eq(UserRoleEntity::getUserId, user.getId()));
        if(CollUtil.isNotEmpty(userDto.getRoleList())){
            insertBatchRole(userDto.getRoleList(), user.getId());
        }
        userDeptDao.delete(Wrappers.<UserDeptEntity>lambdaQuery()
                .eq(UserDeptEntity::getUserId, user.getId()));
        if(CollUtil.isNotEmpty(userDto.getDeptList())){
            insertBatchDept(userDto.getDeptList(), user.getId());
        }
        userPostDao.delete(Wrappers.<UserPostEntity>lambdaQuery()
                .eq(UserPostEntity::getUserId, user.getId()));
        if(CollUtil.isNotEmpty(userDto.getPostList())){
            insertBatchPost(userDto.getPostList(), user.getId());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteUserById(String id) {
        userRoleDao.deleteByUserId(id);
        userDeptDao.deleteByUserId(id);
        userPostDao.deleteByUserId(id);
        userDao.deleteById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUserPassword(UserPasswordDto userPasswordDto) {
        UserEntity userEntity = userDao.selectById(userPasswordDto.getId());
        if(!StrUtil.equals(userEntity.getPassword(), new BCryptPasswordEncoder().encode(userPasswordDto.getOldPassword()))){
            throw new DataException("旧密码不正确");
        }
        String passwordEncode = new BCryptPasswordEncoder().encode(userPasswordDto.getPassword());
        userDao.updateUserPassword(passwordEncode, userPasswordDto.getId());
    }

    @Cacheable(key = "#username")
    @Override
    public UserInfo getUserByUsername(String username) {
        UserInfo userInfo = new UserInfo();
        UserEntity userEntity = userDao.selectOne(Wrappers.<UserEntity>query()
                .lambda().eq(UserEntity::getUsername, username));
        if(null != userEntity){
            UserVo userVo =  userMapper.toVO(userEntity);
            userInfo.setUserVo(userVo);
            if(CollUtil.isNotEmpty(userVo.getRoles())){
                Set<String> permissions = new HashSet<>();
                List<String> roleIds = userVo.getRoles().stream()
                        .map(RoleVo::getId).collect(Collectors.toList());
                List<MenuEntity> menuEntitys = menuDao.selectMenuByRoleIds(roleIds);
                if(CollUtil.isNotEmpty(menuEntitys)){
                    List<String> permissionList = menuEntitys.stream()
                            .filter(menuEntity -> StrUtil.isNotBlank(menuEntity.getMenuPerms()))
                            .map(MenuEntity::getMenuPerms)
                            .collect(Collectors.toList());
                    permissions.addAll(permissionList);
                    userInfo.setPerms(ArrayUtil.toArray(permissions, String.class));
                }
            }
        }
        return userInfo;
    }

    @Override
    public IPage<UserEntity> pageDataScope(IPage<UserEntity> page, Wrapper<UserEntity> queryWrapper, DataScope dataScope) {
        return baseMapper.selectPageDataScope(page, queryWrapper, dataScope);
    }

    @Override
    public void getRouteById(String id) {
        List<MenuEntity> menuEntitys = menuDao.selectMenuByUserId(id);
        if(CollUtil.isNotEmpty(menuEntitys)){
            Set<String> perms = menuEntitys.stream()
                    .filter(menuEntity -> StrUtil.isNotBlank(menuEntity.getMenuPerms()))
                    .map(MenuEntity::getMenuPerms)
                    .collect(Collectors.toSet());
            List<MenuEntity> menus = menuEntitys.stream().filter(menuEntity -> 0 == menuEntity.getMenuType() || 1 == menuEntity.getMenuType())
                    .collect(Collectors.toList());
        }
    }
}
