package com.cf.imes.module.system.service.user;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.cf.imes.framework.common.enums.CommonStatusEnum;
import com.cf.imes.framework.common.enums.UserTypeEnum;
import com.cf.imes.framework.common.exception.ServiceException;
import com.cf.imes.framework.common.exception.util.ServiceExceptionUtil;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.collection.CollectionUtils;
import com.cf.imes.framework.common.util.json.JsonUtils;
import com.cf.imes.framework.common.util.monitor.TracerUtils;
import com.cf.imes.framework.common.util.object.BeanUtils;
import com.cf.imes.framework.common.util.pinyin.PinYinUtils;
import com.cf.imes.framework.common.util.servlet.ServletUtils;
import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.cf.imes.framework.mybatis.core.query.MPJLambdaWrapperX;
import com.cf.imes.framework.organ.core.aop.OrganIgnore;
import com.cf.imes.framework.organ.core.context.OrganContextHolder;
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
import com.cf.imes.module.infra.api.file.FileApi;
import com.cf.imes.module.system.api.logger.dto.LoginLogCreateReqDTO;
import com.cf.imes.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
import com.cf.imes.module.system.api.user.dto.OrganAdminUserRespDTO;
import com.cf.imes.module.system.constants.permission.InternalRoleConstants;
import com.cf.imes.module.system.controller.admin.user.vo.profile.UserMobileUpdateReqVO;
import com.cf.imes.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO;
import com.cf.imes.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO;
import com.cf.imes.module.system.controller.admin.user.vo.user.*;
import com.cf.imes.module.system.dal.dataobject.dept.DeptDO;
import com.cf.imes.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import com.cf.imes.module.system.dal.dataobject.permission.UserRoleDO;
import com.cf.imes.module.system.dal.dataobject.user.AdminUserDO;
import com.cf.imes.module.system.dal.mysql.user.AdminUserMapper;
import com.cf.imes.module.system.enums.ErrorCodeConstants;
import com.cf.imes.module.system.enums.logger.LoginLogTypeEnum;
import com.cf.imes.module.system.enums.logger.LoginResultEnum;
import com.cf.imes.module.system.enums.sms.SmsSceneEnum;
import com.cf.imes.module.system.service.dept.DeptService;
import com.cf.imes.module.system.service.dept.PostService;
import com.cf.imes.module.system.service.logger.LoginLogService;
import com.cf.imes.module.system.service.organ.OrganService;
import com.cf.imes.module.system.service.permission.PermissionService;
import com.cf.imes.module.system.service.sms.SmsCodeService;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import jakarta.annotation.Resource;
import java.io.InputStream;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.regex.Pattern;

import static com.cf.imes.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.cf.imes.framework.common.util.collection.CollectionUtils.convertSet;
import static com.cf.imes.module.system.dal.redis.RedisKeyConstants.OAUTH2_ACCESS_TOKEN;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.*;

/**
 * 后台用户 Service 实现类
 *
 * @author 晨丰科技
 */
@Service("adminUserService")
@Slf4j
public class AdminUserServiceImpl implements AdminUserService {

    @Value("${sys.user.init-password:chenfengyuanma}")
    private String userInitPassword;

    @Resource
    private AdminUserMapper userMapper;

    @Resource
    private DeptService deptService;
    @Resource
    private PostService postService;
    @Resource
    private PermissionService permissionService;
    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    @Lazy // 延迟，避免循环依赖报错
    private OrganService organService;

//    @Resource
//    private UserPostMapper userPostMapper;

    @Resource
    private FileApi fileApi;

    @Resource
    private SmsCodeService smsCodeService;

    @Resource
    private LoginLogService loginLogService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    // 定义密码校验的正则表达式
    private static final String LENGTH_PATTERN = ".{8,}";
    private static final String UPPER_LETTER_PATTERN = ".*[A-Z].*";
    private static final String LOWER_LETTER_PATTERN = ".*[a-z].*";
    private static final String DIGIT_PATTERN = ".*\\d.*";
    private static final String SPECIAL_CHARACTER_PATTERN = ".*[~!@#$%^&*()_+\\-=].*";
    private static final String VALID_CHAR_PATTERN = "^[A-Za-z\\d~!@#$%^&*()_+\\-=]+$";

    /**
     * 获得自身的代理对象，解决 AOP 生效问题
     *
     * @return 自己
     */
    private AdminUserServiceImpl getSelf() {
        return SpringUtil.getBean(getClass());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createUser(UserSaveReqVO createReqVO) {
        Long organId = createReqVO.getOrganId() == null ? OrganContextHolder.getOrganId() : createReqVO.getOrganId();
        // 校验账户配合
        organService.handleOrganInfo(organ -> {
            long count = userMapper.selectCount(new LambdaQueryWrapperX<AdminUserDO>().eq(AdminUserDO::getOrganId, organId));
            if (count >= organ.getAccountCount()) {
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.USER_COUNT_MAX, organ.getAccountCount());
            }
        }, organId);

        // 校验正确性
        validateUserForCreateOrUpdate(null, createReqVO.getMobile(), createReqVO.getDeptId(), null, organId);
        // 插入用户
        AdminUserDO user = BeanUtils.toBean(createReqVO, AdminUserDO.class);
        // 改造：手机号直接当做用户名
        user.setUsername(createReqVO.getMobile());
        user.setOrganId(createReqVO.getOrganId());
        String pinyinFull = PinYinUtils.convertToPinyin(user.getNickname());
        if (StringUtils.isNotBlank(pinyinFull)) {
            pinyinFull = pinyinFull.replace(" ", "");
        }
        user.setPinyinFull(pinyinFull);
        user.setPinyinInitial(PinYinUtils.convertFirstChar(user.getNickname()));
        userMapper.insert(user);
//        // 插入关联岗位
//        if (CollUtil.isNotEmpty(user.getPostIds())) {
//            userPostMapper.insertBatch(convertList(user.getPostIds(),
//                    postId -> new UserPostDO().setUserId(user.getId()).setPostId(postId)));
//        }
        return user.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUser(UserSaveReqVO updateReqVO) {
        Long organId = updateReqVO.getOrganId() == null ? OrganContextHolder.getOrganId() : updateReqVO.getOrganId();
        String mobile = updateReqVO.getMobile();
        Long userId = updateReqVO.getId();
        // 不允许关闭自身状态
        checkCurrentWhenOperateStatus(userId, updateReqVO.getStatus());
        // 校验正确性
        AdminUserDO currUserDO = validateUserForCreateOrUpdate(userId, mobile, updateReqVO.getDeptId(), null, organId);
        // 更新用户
        AdminUserDO updateObj = BeanUtils.toBean(updateReqVO, AdminUserDO.class);

        if (!Objects.isNull(updateReqVO.getNickname())) {

            String pinyinFull = PinYinUtils.convertToPinyin(updateReqVO.getNickname());
            if (StringUtils.isNotBlank(pinyinFull)) {
                pinyinFull = pinyinFull.replace(" ", "");
            }
            updateObj.setPinyinFull(pinyinFull);
            updateObj.setPinyinInitial(PinYinUtils.convertFirstChar(updateReqVO.getNickname()));
        }
        Long id = updateObj.getId();
        if (ObjectUtil.isNotNull(id)) {
            AdminUserDO adminUserDO = userMapper.selectByIdForUpdate(id);
            // 当手机号(账号)发生改变时，只允许密码为空的通过（还没有使用验证码来设置过密码）
            if (ObjectUtil.isNotNull(adminUserDO) && !StringUtils.equals(adminUserDO.getUsername(), mobile)) {
                if(StringUtils.isNotEmpty(adminUserDO.getPassword())) {
                    throw ServiceExceptionUtil.exception(USER_MOBILE_UPDATE_NOT_ALLOW_ERROR);
                } else {
                    updateObj.setUsername(mobile);
                }
            }
        }
        userMapper.updateById(updateObj);
//        // 更新岗位
//        updateUserPost(updateReqVO, updateObj);
        // 如果改变了部门或修改状态为禁用移除用户token
        if (ObjectUtil.notEqual(currUserDO.getDeptId(), updateReqVO.getDeptId()) || CommonStatusEnum.DISABLE.getStatus().equals(updateReqVO.getStatus())) {
            scanAndCompareUserAndDelToken(String.format(OAUTH2_ACCESS_TOKEN, "*"), userId);
        }
    }

    /**
     * 轮训redis token，移除对应用户的token
     *
     * @param keyPattern
     * @param userId
     */
    private void scanAndCompareUserAndDelToken(String keyPattern, Long userId) {
        // 根据keyPattern scan匹配的redis key
        List<String> matchKeys = new ArrayList<>();
        Cursor<String> cursor = stringRedisTemplate.scan(ScanOptions.scanOptions().match(keyPattern).count(200).build());
        while (cursor.hasNext()) {
            matchKeys.add(cursor.next());
        }
        cursor.close();
        if (CollUtil.isNotEmpty(matchKeys)) {
            for (String key : matchKeys) {
                // 获取key下的用户信息
                OAuth2AccessTokenDO oAuth2AccessTokenDO = JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(key), OAuth2AccessTokenDO.class);
                if (ObjectUtil.equal(userId, oAuth2AccessTokenDO.getUserId())) {
                    // 用户id匹配上了删除redis中的token缓存
                    stringRedisTemplate.delete(key);
                    return;
                }
            }
        }
    }

//    private void updateUserPost(UserSaveReqVO reqVO, AdminUserDO updateObj) {
//        Long userId = reqVO.getId();
//        Set<Long> dbPostIds = convertSet(userPostMapper.selectListByUserId(userId), UserPostDO::getPostId);
//        // 计算新增和删除的岗位编号
//        Set<Long> postIds = CollUtil.emptyIfNull(updateObj.getPostIds());
//        Collection<Long> createPostIds = CollUtil.subtract(postIds, dbPostIds);
//        Collection<Long> deletePostIds = CollUtil.subtract(dbPostIds, postIds);
//        // 执行新增和删除。对于已经授权的菜单，不用做任何处理
//        if (!CollUtil.isEmpty(createPostIds)) {
//            userPostMapper.insertBatch(convertList(createPostIds,
//                    postId -> new UserPostDO().setUserId(userId).setPostId(postId)));
//        }
//        if (!CollUtil.isEmpty(deletePostIds)) {
//            userPostMapper.deleteByUserIdAndPostId(userId, deletePostIds);
//        }
//    }

    @Override
    public void updateUserLogin(Long id, String loginIp) {
        userMapper.update(new LambdaUpdateWrapper<AdminUserDO>()
                .eq(AdminUserDO::getId, id)
                .set(AdminUserDO::getLoginIp, loginIp)
                .set(AdminUserDO::getLoginDate, LocalDateTime.now()));
    }

    @Override
    public void updateUserProfile(Long id, UserProfileUpdateReqVO reqVO) {
        // 校验正确性
        validateUserExists(id);
        // 执行更新
        userMapper.updateById(BeanUtils.toBean(reqVO, AdminUserDO.class).setId(id));
    }

    @Override
    public void updateUserPassword(Long id, UserProfileUpdatePasswordReqVO reqVO) {
        // 校验旧密码密码
        validateOldPassword(id, reqVO.getOldPassword());
        // 校验密码是否符合要求
        validatePassword(reqVO.getNewPassword());
        // 执行更新
        AdminUserDO updateObj = new AdminUserDO().setId(id);
        updateObj.setPassword(encodePassword(reqVO.getNewPassword())); // 加密密码
        userMapper.updateById(updateObj);
    }

    @Override
    public String updateUserAvatar(Long id, InputStream avatarFile) {
        validateUserExists(id);
        // 存储文件
        String avatar = fileApi.createFile(IoUtil.readBytes(avatarFile));
        // 更新路径
        AdminUserDO sysUserDO = new AdminUserDO();
        sysUserDO.setId(id);
        sysUserDO.setAvatar(avatar);
        userMapper.updateById(sysUserDO);
        return avatar;
    }

    @Override
    public void updateUserPassword(Long id, String password) {
        // 校验用户存在
        validateUserExists(id);
        // 更新密码
        AdminUserDO updateObj = new AdminUserDO();
        updateObj.setId(id);
        // 校验密码
        validatePassword(password);
        updateObj.setPassword(encodePassword(password)); // 加密密码
        userMapper.updateById(updateObj);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void resetUserPassword(UserResetPasswordReqVO reqVO) {
        String mobile = reqVO.getMobile();
        String password = reqVO.getPassword();
        // 校验用户名
        AdminUserDO adminUserDO = validateUserNameExists(mobile);
        Long userId = adminUserDO.getId();
        String username = adminUserDO.getUsername();
        // 校验密码
        validatePassword(password);
        // 更新密码
        AdminUserDO updateObj = new AdminUserDO();
        updateObj.setId(userId);
        updateObj.setPassword(encodePassword(password)); // 加密密码
        userMapper.updateById(updateObj);

        // 记录日志
        createLoginLog(userId, username, LoginLogTypeEnum.LOGIN_FORGET_PWD, LoginResultEnum.SUCCESS);
        // 消耗验证码
        smsCodeService.useSmsCode(SmsCodeUseReqDTO.builder()
                .mobile(mobile).code(reqVO.getSmsCaptchaVerification()).scene(SmsSceneEnum.USER_RESET_PASSWORD.getScene())
                .build());
    }

    private void createLoginLog(Long userId, String username,
                                LoginLogTypeEnum logTypeEnum, LoginResultEnum loginResult) {
        // 插入登录日志
        LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO();
        reqDTO.setLogType(logTypeEnum.getType());
        reqDTO.setTraceId(TracerUtils.getTraceId());
        reqDTO.setUserId(userId);
        reqDTO.setUserType(UserTypeEnum.ADMIN.getValue());
        reqDTO.setUsername(username);
        reqDTO.setUserAgent(ServletUtils.getUserAgent());
        reqDTO.setUserIp(ServletUtils.getClientIP());
        reqDTO.setResult(loginResult.getResult());
        loginLogService.createLoginLog(reqDTO);
    }

    @Override
    public void updateUserStatus(Long id, Integer status) {
        // 不允许自身操作
        checkCurrentWhenOperate(id);
        // 校验用户存在
        validateUserExists(id);
        // 更新状态
        AdminUserDO updateObj = new AdminUserDO();
        updateObj.setId(id);
        updateObj.setStatus(status);
        userMapper.updateById(updateObj);
        if (CommonStatusEnum.DISABLE.getStatus().equals(status)) {
            // 移除用户token
            scanAndCompareUserAndDelToken(String.format(OAUTH2_ACCESS_TOKEN, "*"), id);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteUser(Long id) {
        // 不允许自身操作
        checkCurrentWhenOperate(id);
        // 校验用户存在
        validateUserExists(id);
        // 物理删除用户
        int deleteNum = userMapper.physicalDeleteById(id);
        if (deleteNum > 0) {
            // 删除用户关联数据
            permissionService.processUserDeleted(id);
//            // 删除用户岗位
//            userPostMapper.deleteByUserId(id);
            // 移除用户token
            scanAndCompareUserAndDelToken(String.format(OAUTH2_ACCESS_TOKEN, "*"), id);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteOrgUsers(Long organId) {
        // 查询机构下所有的用户id
        List<Long> userIds = userMapper.selectAllOrganUsers(organId);
        for (Long userId : userIds) {
            // 物理删除用户
            int deleteNum = userMapper.physicalDeleteById(userId);
            if (deleteNum > 0) {
                // 删除用户关联数据
                permissionService.processUserDeleted(userId);
//                // 删除用户岗位
//                userPostMapper.deleteByUserId(userId);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteDeptUsers(Long deptId) {
        // 查询部门下所有的用户id
        List<Long> userIds = userMapper.selectAllDeptUsers(deptId);
        for (Long userId : userIds) {
            // 物理删除用户
            int deleteNum = userMapper.physicalDeleteById(userId);
            if (deleteNum > 0) {
                // 删除用户关联数据
                permissionService.processUserDeleted(userId);
//                // 删除用户岗位
//                userPostMapper.deleteByUserId(userId);
            }
        }
    }

    @Override
    public AdminUserDO getUserByUsername(String username, Long organId) {
        return userMapper.selectByUsername(username, organId);
    }

    @Override
    @OrganIgnore
    public AdminUserDO getUserByUsernameAndOrganId(String username, Long organId) {
        return userMapper.selectOne(new LambdaQueryWrapperX<AdminUserDO>()
                .eq(AdminUserDO::getUsername, username)
                .eqIfPresent(AdminUserDO::getOrganId, organId)
        );
    }

    @Override
    public AdminUserDO getUserUniqueByUserName(String userName) {
        return userMapper.selectByUsernameUnique(userName);
    }

    @Override
    public AdminUserDO getUserByMobile(String mobile) {
        return userMapper.selectByMobile(mobile);
    }

    @Override
    public PageResult<AdminUserDO> getUserPage(UserPageReqVO reqVO) {
        if (StringUtils.isNotBlank(reqVO.getNickname())) {
            reqVO.setPyFirstChar(PinYinUtils.convertFirstChar(reqVO.getNickname()));
            reqVO.setPyAll(PinYinUtils.convertToPinyin(reqVO.getNickname()).replace(" ", ""));
        } else {
            // 避免name为空外部传入拼音查询字段
            reqVO.setPyFirstChar(null);
            reqVO.setPyAll(null);
        }

        PageResult<AdminUserDO> adminUserDOPageResult = userMapper.selectPage(reqVO, getDeptCondition(reqVO.getDeptId()));

        return adminUserDOPageResult;
    }

    @Override
    public AdminUserDO getUser(Long id) {
        return userMapper.selectById(id);
    }

    @Override
    public List<AdminUserDO> getUserListByDeptIds(Collection<Long> deptIds) {
        if (CollUtil.isEmpty(deptIds)) {
            return Collections.emptyList();
        }
        return userMapper.selectListByDeptIds(deptIds);
    }

    @Override
    public List<AdminUserDO> getUserListByPostIds(Collection<Long> postIds) {
        if (CollUtil.isEmpty(postIds)) {
            return Collections.emptyList();
        }
//        Set<Long> userIds = convertSet(userPostMapper.selectListByPostIds(postIds), UserPostDO::getUserId);
//        if (CollUtil.isEmpty(userIds)) {
//            return Collections.emptyList();
//        }
//        return userMapper.selectBatchIds(userIds);
        return null;
    }

    @Override
    public List<AdminUserDO> getUserList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        return userMapper.selectBatchIds(ids);
    }

    @Override
    public void validateUserList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return;
        }
        // 获得用户信息
        List<AdminUserDO> users = userMapper.selectBatchIds(ids);
        Map<Long, AdminUserDO> userMap = CollectionUtils.convertMap(users, AdminUserDO::getId);
        // 校验
        ids.forEach(id -> {
            AdminUserDO user = userMap.get(id);
            if (user == null) {
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.USER_NOT_EXISTS);
            }
            if (!CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus())) {
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.USER_IS_DISABLE, user.getNickname());
            }
        });
    }

    @Override
    public List<AdminUserDO> getUserListByNickname(String nickname) {
        return userMapper.selectListByNickname(nickname);
    }

    /**
     * 获得部门条件：查询指定部门的子部门编号们，包括自身
     *
     * @param deptId 部门编号
     * @return 部门编号集合
     */
    private Set<Long> getDeptCondition(Long deptId) {
        if (deptId == null) {
            return Collections.emptySet();
        }
        Set<Long> deptIds = convertSet(deptService.getChildDeptList(deptId), DeptDO::getId);
        deptIds.add(deptId); // 包括自身
        return deptIds;
    }

    private AdminUserDO validateUserForCreateOrUpdate(Long id, String username, Long deptId, String password, Long organId) {
        // 校验用户的密码是否符合规则
        if (password != null) {
            validatePassword(password);
        }
        // 校验用户存在
        AdminUserDO adminUserDO = validateUserExists(id);
        // 校验用户名唯一
        getSelf().validateUsernameUnique(id, username, organId);
        // 组织管理员不归属部门
        if (ObjectUtil.isNotNull(deptId)) {
            // 校验部门处于开启状态
            deptService.validDept(deptId);
        }
//        // 校验岗位处于开启状态
//        postService.validatePostList(postIds);
        return adminUserDO;
    }

    @VisibleForTesting
    void validatePassword(String password) {

        // 检查密码长度
        if (!Pattern.matches(LENGTH_PATTERN, password)) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.THE_PASSWORD_LENGTH_MUST_BE_AT_LEAST_8_CHARACTERS);
        }
        // 检查是否包含大写字母
        if (!Pattern.matches(UPPER_LETTER_PATTERN, password)) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.THE_PASSWORD_LENGTH_MUST_BE_AT_LEAST_8_CHARACTERS);
        }
        // 检查是否包含小写字母
        if (!Pattern.matches(LOWER_LETTER_PATTERN, password)) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.THE_PASSWORD_LENGTH_MUST_BE_AT_LEAST_8_CHARACTERS);
        }
        // 检查是否包含数字
        if (!Pattern.matches(DIGIT_PATTERN, password)) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.THE_PASSWORD_LENGTH_MUST_BE_AT_LEAST_8_CHARACTERS);
        }
        // 检查是否包含特殊字符
        if (!Pattern.matches(SPECIAL_CHARACTER_PATTERN, password)) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.THE_PASSWORD_LENGTH_MUST_BE_AT_LEAST_8_CHARACTERS);
        }
        // 检查是否只包含字母、数字和特殊字符
        if (!Pattern.matches(VALID_CHAR_PATTERN, password)) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.THE_PASSWORD_LENGTH_MUST_BE_AT_LEAST_8_CHARACTERS);
        }

    }

    @VisibleForTesting
    AdminUserDO validateUserExists(Long id) {
        if (id == null) {
            return null;
        }
        AdminUserDO user = userMapper.selectById(id);
        if (user == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.USER_NOT_EXISTS);
        }
        return user;
    }

    @VisibleForTesting
    private AdminUserDO validateUserNameExists(String userName) {
        AdminUserDO user = userMapper.selectByUsernameUnique(userName);
        if (user == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.USER_NOT_EXISTS);
        }
        return user;
    }

    @VisibleForTesting
    @OrganIgnore
    void validateUsernameUnique(Long id, String username, Long organId) {
        if (StringUtils.isBlank(username)) {
            return;
        }
        AdminUserDO user = userMapper.selectOne(AdminUserDO::getUsername, username);
        if (user == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的用户
        if (id == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.USER_USERNAME_EXISTS);
        }
        if (!user.getId().equals(id)) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.USER_USERNAME_EXISTS);
        }
    }

    @VisibleForTesting
    void validateEmailUnique(Long id, String email, Long organId) {
        if (StringUtils.isBlank(email)) {
            return;
        }
        AdminUserDO user = userMapper.selectOne(AdminUserDO::getOrganId, organId, AdminUserDO::getEmail, email);
        if (user == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的用户
        if (id == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.USER_EMAIL_EXISTS);
        }
        if (!user.getId().equals(id)) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.USER_EMAIL_EXISTS);
        }
    }

    @VisibleForTesting
    @OrganIgnore
    AdminUserDO validateMobileUnique(Long id, String mobile) {
        AdminUserDO user = userMapper.selectOne(AdminUserDO::getUsername, mobile);
        if (user == null) {
            return user;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的用户
        if (id == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.USER_MOBILE_EXISTS);
        }
        if (!user.getId().equals(id)) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.USER_MOBILE_EXISTS);
        }
        return user;
    }

    /**
     * 校验旧密码
     *
     * @param id          用户 id
     * @param oldPassword 旧密码
     */
    @VisibleForTesting
    void validateOldPassword(Long id, String oldPassword) {
        AdminUserDO user = userMapper.selectById(id);
        if (user == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.USER_NOT_EXISTS);
        }
        if (!isPasswordMatch(oldPassword, user.getPassword())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.USER_PASSWORD_FAILED);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class) // 添加事务，异常则回滚所有导入
    public UserImportRespVO importUserList(List<UserImportExcelVO> importUsers, boolean isUpdateSupport, Long organId) {
        if (CollUtil.isEmpty(importUsers)) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.USER_IMPORT_LIST_IS_EMPTY);
        }
        UserImportRespVO respVO = UserImportRespVO.builder().createUsernames(new ArrayList<>())
                .updateUsernames(new ArrayList<>()).failureUsernames(new LinkedHashMap<>()).build();
        importUsers.forEach(importUser -> {
            // 校验，判断是否有不符合的原因
            try {
                validateUserForCreateOrUpdate(null, null, importUser.getDeptId(), null, organId);
            } catch (ServiceException ex) {
                respVO.getFailureUsernames().put(importUser.getUsername(), ex.getMessage());
                return;
            }
            // 判断如果不存在，在进行插入
            AdminUserDO existUser = userMapper.selectByUsername(importUser.getUsername(), organId);
            if (existUser == null) {
                // 改造：手机号直接当做用户名
                importUser.setUsername(importUser.getMobile());
                userMapper.insert(BeanUtils.toBean(importUser, AdminUserDO.class)
                        .setPassword(encodePassword(userInitPassword)).setPostIds(new HashSet<>())); // 设置默认密码及空岗位编号数组
                respVO.getCreateUsernames().add(importUser.getUsername());
                return;
            }
            // 如果存在，判断是否允许更新
            if (!isUpdateSupport) {
                respVO.getFailureUsernames().put(importUser.getUsername(), ErrorCodeConstants.USER_USERNAME_EXISTS.getMsg());
                return;
            }
            AdminUserDO updateUser = BeanUtils.toBean(importUser, AdminUserDO.class);
            updateUser.setId(existUser.getId());
            userMapper.updateById(updateUser);
            respVO.getUpdateUsernames().add(importUser.getUsername());
        });
        return respVO;
    }

    @Override
    public List<AdminUserDO> getUserListByStatus(Integer status, Long organId, Long deptId) {
        return userMapper.selectListByStatus(status, organId, deptId);
    }

    @Override
    public boolean isPasswordMatch(String rawPassword, String encodedPassword) {
        return passwordEncoder.matches(rawPassword, encodedPassword);
    }

    @Override
    public List<AdminUserDO> getUserListByTerms(Integer status, String name) {
        String namePyFirstChar = "";
        String namePyAll = "";
        if (StringUtils.isNotBlank(name)) {
            namePyFirstChar = PinYinUtils.convertToPinyin(name);
            namePyAll = PinYinUtils.convertToPinyin(name).replace(" ", "");
        }
        return userMapper.selectListByTerms(status, namePyAll, namePyFirstChar, name, SecurityFrameworkUtils.getUserOrganId());
    }

    @Override
    @OrganIgnore
    public List<OrganAdminUserRespDTO> getOrganAdminByOrganIds(Collection<Long> organIds) {
        if (CollUtil.isEmpty(organIds)) {
            return new ArrayList<>();
        }
        MPJLambdaWrapperX<AdminUserDO> wrapperX = new MPJLambdaWrapperX<>();
        wrapperX.leftJoin(UserRoleDO.class, UserRoleDO::getUserId, AdminUserDO::getId)
                .eq(UserRoleDO::getRoleId, InternalRoleConstants.ORGAN_ADMIN_ROLE_ID)
        ;
        List<OrganAdminUserRespDTO> organAdminUserRespDTOS = userMapper.selectJoinList(OrganAdminUserRespDTO.class, wrapperX);
        return organAdminUserRespDTOS;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUserSecurityPhone(Long id, UserMobileUpdateReqVO reqVO) {
        String mobile = reqVO.getMobile();
        // 校验手机号唯一
        AdminUserDO adminUserDO = validateMobileUnique(id, mobile);
        // 手机号没有改变无需请求
        if (ObjectUtil.isNotNull(adminUserDO) && ObjectUtil.equal(id, adminUserDO.getId())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.AUTH_MOBILE_NO_CHANGE);
        }

        // 新手机号入库 -> setUsername
        userMapper.update(new LambdaUpdateWrapper<AdminUserDO>()
                .set(AdminUserDO::getUsername, mobile)
                .set(AdminUserDO::getMobile, mobile)
                .eq(AdminUserDO::getId, id));

        // 校验和消耗验证码
        smsCodeService.useSmsCode(SmsCodeUseReqDTO.builder()
                .mobile(reqVO.getMobile()).code(reqVO.getSmsCaptchaVerification()).scene(SmsSceneEnum.USER_UPDATE_MOBILE.getScene())
                .build());
    }

    /**
     * 对密码进行加密
     *
     * @param password 密码
     * @return 加密后的密码
     */
    private String encodePassword(String password) {
        return passwordEncoder.encode(password);
    }

    /**
     * 检查是否当前用户操作
     */
    private void checkCurrentWhenOperate(Long userId) {
        if (ObjectUtil.equal(userId, SecurityFrameworkUtils.getLoginUserId())) {
            throw exception(USER_ME_ERROR);
        }
    }

    private void checkCurrentWhenOperateStatus(Long userId, Integer status) {
        if (ObjectUtil.equal(userId, SecurityFrameworkUtils.getLoginUserId()) && Objects.equals(CommonStatusEnum.DISABLE.getStatus(), status)) {
            throw exception(USER_OPERATE_SELF_STATUS_ERROR);
        }
    }

    @Override
    public Map<String, Integer> userTotal() {
        Map<String, Integer> map = new HashMap<>();

        // 用户总数
        Integer userTotal = userMapper.selectUserCount();
        map.put("userTotal", userTotal);
        // 当日新增数
        Integer userTodayAdd = userMapper.selectUserCountAdd();
        map.put("userTodayAdd", userTodayAdd);
        // 当日状态-停止数
        Integer userTodayDel = userMapper.selectUserCountDel();
        map.put("userTodayDel", userTodayDel);
        // 当日沉寂数 (30天未登录)
        Date date = DateUtil.offsetDay(new Date(), -30);
        Integer userTodaySilent = userMapper.selectUserCountSilent(DateUtil.beginOfDay(date).toLocalDateTime());
        map.put("userTodaySilent", userTodaySilent);

        return map;
    }

    @Override
    public Map<String, Integer> userActTotal() {
        Map<String, Integer> map = new HashMap<>();

        LocalDateTime startTime = getFirstDayOfMonth();
        LocalDateTime endTIme = LocalDateTime.of(LocalDate.now(), LocalTime.of(23, 59, 59));
        // 用户月活跃数
        Integer userMonthAct = userMapper.selectUserActCountTime(startTime, endTIme);
        map.put("userMonthAct", userMonthAct);
        // 用户日活跃数
        Integer userDayAct = userMapper.selectUserActCountTime(LocalDateTime.of(LocalDate.now(), LocalTime.of(0, 0, 0)), endTIme);
        map.put("userDayAct", userDayAct);
        return map;
    }

    //    获得此月第一天日期
    public LocalDateTime getFirstDayOfMonth() {
        // 获取当前日期
        LocalDate today = LocalDate.now();

        // 获取当前月份的第一天
        LocalDate firstDayOfMonth = today.withDayOfMonth(1);

        // 将时间设置为 0 点
        return LocalDateTime.of(firstDayOfMonth, LocalTime.MIDNIGHT);
    }

    @Override
    @OrganIgnore
    public List<AdminUserDO> getOrganUsers(Long organId) {
        return userMapper.selectList(new LambdaQueryWrapper<AdminUserDO>().eq(AdminUserDO::getOrganId, organId));
    }
}
