package com.xjrsoft.module.organization.service.impl;

import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.type.TypeReference;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.xjrsoft.common.constant.GlobalConstant;
import com.xjrsoft.common.exception.MyException;
import com.xjrsoft.common.page.ConventPage;
import com.xjrsoft.common.page.PageOutput;
import com.xjrsoft.common.utils.RedisUtil;
import com.xjrsoft.common.utils.VoToColumnUtil;
import com.xjrsoft.config.CommonPropertiesConfig;
import com.xjrsoft.module.organization.dto.AddUserDto;
import com.xjrsoft.module.organization.dto.BindOpenidDto;
import com.xjrsoft.module.organization.dto.ResetPasswordDto;
import com.xjrsoft.module.organization.dto.UpdatePasswordDto;
import com.xjrsoft.module.organization.dto.UpdateUserDto;
import com.xjrsoft.module.organization.dto.UserPageDto;
import com.xjrsoft.module.organization.dto.WeChatPageDto;
import com.xjrsoft.module.organization.entity.User;
import com.xjrsoft.module.organization.entity.UserDeptRelation;
import com.xjrsoft.module.organization.entity.UserPostRelation;
import com.xjrsoft.module.organization.entity.UserRoleRelation;
import com.xjrsoft.module.organization.mapper.UserDeptRelationMapper;
import com.xjrsoft.module.organization.mapper.UserMapper;
import com.xjrsoft.module.organization.mapper.UserPostRelationMapper;
import com.xjrsoft.module.organization.mapper.UserRoleRelationMapper;
import com.xjrsoft.module.organization.service.IUserDeptRelationService;
import com.xjrsoft.module.organization.service.IUserService;
import com.xjrsoft.module.organization.vo.ResetUserPageVo;
import com.xjrsoft.module.organization.vo.UserInfoVo;
import com.xjrsoft.module.organization.vo.WeChatPageVO;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户 服务实现类
 * </p>
 *
 * @author tzx
 * @since 2022-03-02
 */
@Service
@AllArgsConstructor
public class UserServiceImpl extends MPJBaseServiceImpl<UserMapper, User> implements IUserService {

    private final UserMapper userMapper;

    private final UserDeptRelationMapper userDeptRelationMapper;

    private final IUserDeptRelationService userDeptRelationService;

    private final UserRoleRelationMapper userRoleRelationMapper;

    private final UserPostRelationMapper userPostRelationMapper;

    private final RedisUtil redisUtil;

    private final CommonPropertiesConfig propertiesConfig;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean add(AddUserDto dto) {
        long count = count(Wrappers.<User>query().lambda().eq(User::getUserName, dto.getUserName()).or().eq(User::getCode, dto.getCode()));
        if (count > 0) {
            throw new MyException("用户名称或编码已存在");
        }
        count = count(Wrappers.<User>query().lambda().eq(User::getMobile, dto.getMobile()));
        if (count > 0) {
            throw new MyException("手机号码已存在");
        }
        User user = BeanUtil.toBean(dto, User.class);

        //密码加密加盐存储到数据库
        user.setPassword(BCrypt.hashpw(dto.getPassword(), BCrypt.gensalt()));
        save(user);

        // 添加角色
        if (dto.getRoleIds() != null) {
            for (Long roleId : dto.getRoleIds()) {
                UserRoleRelation userRoleRelation = new UserRoleRelation();
                userRoleRelation.setUserId(user.getId());
                userRoleRelation.setRoleId(roleId);
                userRoleRelationMapper.insert(userRoleRelation);
            }
        }

        List<UserDeptRelation> userDeptRelationList = new ArrayList<>();
        if (StrUtil.isNotBlank(dto.getDepartmentIds())) {
            String allDeptIdStr = StrUtil.join(StringPool.COMMA, dto.getDepartmentIds());
            List<Long> departmentIds = Arrays.stream(allDeptIdStr.split(StringPool.COMMA)).map(Convert::toLong).collect(Collectors.toList());
            if (CollectionUtil.isNotEmpty(departmentIds)) {
                for (Long deptId : departmentIds) {
                    //将用户所选部门保存到关联表中
                    UserDeptRelation userDeptRelation = new UserDeptRelation();
                    userDeptRelation.setUserId(user.getId());
                    userDeptRelation.setDeptId(deptId);
                    userDeptRelationList.add(userDeptRelation);
                }
            }
            userDeptRelationService.saveBatch(userDeptRelationList);
        }

        CompletableFuture.runAsync(() -> {
            List<User> list = list();
            redisUtil.set(GlobalConstant.USER_CACHE_KEY, list);

            List<UserDeptRelation> deptRelationList = userDeptRelationMapper.selectList(Wrappers.lambdaQuery(UserDeptRelation.class));
            redisUtil.set(GlobalConstant.USER_DEPT_RELATION_CACHE_KEY, deptRelationList);
        });

        return Boolean.TRUE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean update(UpdateUserDto dto) {
        long count = count(Wrappers.<User>query().lambda()
                .eq(User::getCode, dto.getCode())
                .ne(User::getId, dto.getId()));

        if (count > 0) {
            throw new MyException("用户名称或编码已存在");
        }

        User user = BeanUtil.toBean(dto, User.class);

        updateById(user);

        //先删除再新增
        userDeptRelationService.remove(Wrappers.<UserDeptRelation>query().lambda().eq(UserDeptRelation::getUserId, user.getId()));
        List<UserDeptRelation> userDeptRelationList = new ArrayList<>();
        if (StrUtil.isNotBlank(dto.getDepartmentIds())) {
            String allDeptIdStr = StrUtil.join(StringPool.COMMA, dto.getDepartmentIds());
            List<Long> departmentIds = Arrays.stream(allDeptIdStr.split(StringPool.COMMA)).map(Convert::toLong).collect(Collectors.toList());
            if (CollectionUtil.isNotEmpty(departmentIds)) {
                for (Long deptId : departmentIds) {
                    //将用户所选部门保存到关联表中
                    UserDeptRelation userDeptRelation = new UserDeptRelation();
                    userDeptRelation.setUserId(user.getId());
                    userDeptRelation.setDeptId(deptId);
                    userDeptRelationList.add(userDeptRelation);
                }
            }
            userDeptRelationService.saveBatch(userDeptRelationList);
        }

        //先删除再新增
        userRoleRelationMapper.delete(Wrappers.<UserRoleRelation>query().lambda().eq(UserRoleRelation::getUserId, user.getId()));
        if (ObjectUtil.isNotNull(dto.getRoleIds())) {
            for (Long roleId : dto.getRoleIds()) {
                //将用户所选部门保存到关联表中
                UserRoleRelation userRoleRelation = new UserRoleRelation();
                userRoleRelation.setUserId(user.getId());
                userRoleRelation.setRoleId(roleId);

                userRoleRelationMapper.insert(userRoleRelation);
            }
        }


        CompletableFuture.runAsync(() -> {
            List<User> list = list();
            redisUtil.set(GlobalConstant.USER_CACHE_KEY, list);

            List<UserDeptRelation> deptRelationList = userDeptRelationMapper.selectList(Wrappers.lambdaQuery(UserDeptRelation.class));
            redisUtil.set(GlobalConstant.USER_DEPT_RELATION_CACHE_KEY, deptRelationList);
        });
        return Boolean.TRUE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteBatch(List<Long> ids) {
        //删除时需要同时删除用户部门关联表和用户角色关联表和用户岗位关系表数据。
        this.removeBatchByIds(ids);
        //根据用户ids去缓存中查询到对应的三个表的数据
        List<UserDeptRelation> userDeptRelationList = redisUtil.get(GlobalConstant.USER_DEPT_RELATION_CACHE_KEY, new TypeReference<List<UserDeptRelation>>() {
        });
        List<UserPostRelation> userPostRelationList = redisUtil.get(GlobalConstant.USER_POST_RELATION_CACHE_KEY, new TypeReference<List<UserPostRelation>>() {
        });
        List<UserRoleRelation> userRoleRelationList = redisUtil.get(GlobalConstant.USER_ROLE_RELATION_CACHE_KEY, new TypeReference<List<UserRoleRelation>>() {
        });
        //拿用户ids进行过滤，如果存在，就删除
        List<Long> deptRelationIds = userDeptRelationList.stream().filter(u -> ids.contains(u.getUserId())).map(UserDeptRelation::getId).collect(Collectors.toList());
        List<Long> postRelationIds = userPostRelationList.stream().filter(u -> ids.contains(u.getUserId())).map(UserPostRelation::getId).collect(Collectors.toList());
        List<Long> roleRelationIds = userRoleRelationList.stream().filter(u -> ids.contains(u.getUserId())).map(UserRoleRelation::getId).collect(Collectors.toList());
        //调用三个表的删除
        if (CollectionUtil.isNotEmpty(deptRelationIds)) {
            userDeptRelationMapper.deleteBatchIds(deptRelationIds);
        }
        if (CollectionUtil.isNotEmpty(postRelationIds)) {
            userPostRelationMapper.deleteBatchIds(postRelationIds);
        }
        if (CollectionUtil.isNotEmpty(roleRelationIds)) {
            userRoleRelationMapper.deleteBatchIds(roleRelationIds);
        }
        //更新缓存
        CompletableFuture.runAsync(() -> {
            List<User> list = this.list();
            redisUtil.set(GlobalConstant.USER_CACHE_KEY, list);

            List<UserDeptRelation> deptRelationList = userDeptRelationMapper.selectList(Wrappers.lambdaQuery(UserDeptRelation.class));
            redisUtil.set(GlobalConstant.USER_DEPT_RELATION_CACHE_KEY, deptRelationList);

            List<UserPostRelation> postRelationList = userPostRelationMapper.selectList(Wrappers.lambdaQuery(UserPostRelation.class));
            redisUtil.set(GlobalConstant.USER_POST_RELATION_CACHE_KEY, postRelationList);

            List<UserRoleRelation> roleRelationList = userRoleRelationMapper.selectList(Wrappers.lambdaQuery(UserRoleRelation.class));
            redisUtil.set(GlobalConstant.USER_ROLE_RELATION_CACHE_KEY, roleRelationList);
        });
        return Boolean.TRUE;
    }

    @Override
    public boolean updatePassword(UpdatePasswordDto dto) {

        User updateUser = new User();
        updateUser.setId(StpUtil.getLoginIdAsLong());
        updateUser.setPassword(BCrypt.hashpw(dto.getNewPassword(), BCrypt.gensalt()));
        updateUser.setIsChangePassword(0);

        return updateById(updateUser);

    }

    @Override
    public boolean resetPassword(ResetPasswordDto dto) {
        User user = new User();
        user.setId(dto.getId());
        user.setPassword(BCrypt.hashpw(propertiesConfig.getDefaultPassword(), BCrypt.gensalt()));
        user.setIsChangePassword(1);

        CompletableFuture.runAsync(() -> {
            List<User> list = list();
            redisUtil.set(GlobalConstant.USER_CACHE_KEY, list);
        });

        return updateById(user);
    }

    @Override
    public boolean batchResetPassword(List<Long> ids) {
        for (Long id : ids) {
            User user = new User();
            user.setId(id);
            user.setPassword(BCrypt.hashpw(propertiesConfig.getDefaultPassword(), BCrypt.gensalt()));
            user.setIsChangePassword(1);

            CompletableFuture.runAsync(() -> {
                List<User> list = list();
                redisUtil.set(GlobalConstant.USER_CACHE_KEY, list);
            });
            updateById(user);
        }

        return true;
    }

    @Override
    public boolean bindOpenid(BindOpenidDto dto) {
        User user = this.getOne(Wrappers.<User>query().lambda().select(User::getId, User::getOpenId, User::getUnionId).eq(User::getId, dto.getId()), false);
        if (user != null) {
            if (StrUtil.isNotBlank(user.getOpenId())) {
                throw new MyException("该用户已经绑定微信");
            }
            long count = this.count(Wrappers.<User>query().lambda().eq(User::getOpenId, dto.getOpenid()));
            if (count > 0) {
                throw new MyException("该用户已经绑定微信！");
            }
            User updateUser = new User();
            updateUser.setId(dto.getId());
            updateUser.setOpenId(dto.getOpenid());
            updateUser.setUnionId(dto.getUnionid());
            boolean result = this.updateById(updateUser);
            // 用户绑定微信后，更新缓存
            if (result) {
                CompletableFuture.runAsync(() -> {
                    List<User> list = list();
                    redisUtil.set(GlobalConstant.USER_CACHE_KEY, list);
                });
            }
            return result;
        } else {
            long count = this.count(Wrappers.<User>query().lambda().eq(User::getOpenId, dto.getOpenid()));
            if (count > 0) {
                throw new MyException("该用户已经绑定微信！");
            }
        }
        throw new MyException("该用户不存在！");
    }

    @Override
    public boolean unbindOpenid(Long id) {
        User updateUser = new User();
        updateUser.setId(id);
        updateUser.setOpenId("");
        updateUser.setUnionId("");
        boolean result = updateById(updateUser);
        // 用户绑定微信后，更新缓存
        if (result) {
            CompletableFuture.runAsync(() -> {
                List<User> list = list();
                redisUtil.set(GlobalConstant.USER_CACHE_KEY, list);
            });
        }
        return result;
    }

    @Override
    public List<UserInfoVo> getUsersInfo(String ids) {

        if (StrUtil.isBlank(ids)) {
            return new ArrayList<>();
        }
        List<Long> idList = Arrays.stream(ids.split(",")).map(Long::parseLong).collect(Collectors.toList());

        List<User> list = userMapper.selectList(Wrappers.lambdaQuery(User.class)
                .in(User::getId, idList)
                .select(User.class, x -> VoToColumnUtil.fieldsToColumns(UserInfoVo.class).contains(x.getProperty())));

        return BeanUtil.copyToList(list, UserInfoVo.class);
    }

    @Override
    public PageOutput<WeChatPageVO> getPage(WeChatPageDto dto) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StrUtil.isNotBlank(dto.getKeyword()), User::getName, dto.getKeyword())
                .select(User.class, x -> VoToColumnUtil.fieldsToColumns(WeChatPageVO.class).contains(x.getProperty()));
        IPage<User> page = userMapper.selectPage(ConventPage.getPage(dto), queryWrapper);
        List<User> records = page.getRecords();
        for (User record : records) {
            record.setRemark("员工更新成功");
            record.setSortCode(1);
        }
        return ConventPage.getPageOutput(page, WeChatPageVO.class);

    }

    @Override
    public PageOutput<ResetUserPageVo> getResetUserPage(UserPageDto dto){
        Page<ResetUserPageVo> resetUserPage = userMapper.getResetUserPage(new Page<>(dto.getLimit(), dto.getSize()), dto);
        PageOutput<ResetUserPageVo> pageOutput = ConventPage.getPageOutput(resetUserPage, ResetUserPageVo.class);
        return pageOutput;
    }
}
