package com.liquidnet.service.adam.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.liquidnet.common.cache.redis.util.RedisUtil;
import com.liquidnet.common.exception.constant.ErrorCode;
import com.liquidnet.commons.lang.util.DESUtils;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.service.ResponseDto;
import com.liquidnet.service.adam.biz.AdamUserLoginBiz;
import com.liquidnet.service.adam.common.AdamErrorCode;
import com.liquidnet.service.adam.constant.*;
import com.liquidnet.service.adam.dto.*;
import com.liquidnet.service.adam.dto.base.AdamCurrentUser;
import com.liquidnet.service.adam.dto.base.AdamResultDto;
import com.liquidnet.service.adam.dto.query.AdamCommonQuery;
import com.liquidnet.service.adam.dto.query.AdamUserQuery;
import com.liquidnet.service.adam.entity.AdamAccountWallet;
import com.liquidnet.service.adam.entity.AdamRolePermission;
import com.liquidnet.service.adam.entity.AdamUser;
import com.liquidnet.service.adam.entity.AdamUserAuthLog;
import com.liquidnet.service.adam.incrementer.CustomIdGenerator;
import com.liquidnet.service.adam.mapper.AdamUserMapper;
import com.liquidnet.service.adam.service.*;
import com.liquidnet.service.adam.service.sys.IAdamSysMessageService;
import com.liquidnet.service.adam.service.sys.IAdamSystemService;
import com.liquidnet.service.adam.util.EmailUtil;
import com.liquidnet.service.adam.util.EnumUtil;
import com.liquidnet.service.adam.vo.AdamAdminisProcessHisVo;
import com.liquidnet.service.adam.vo.AdamUserProfileVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户信息 服务实现类
 * </p>
 *
 * @author liquidnet
 * @since 2020-09-16
 */
@Slf4j
@Service
public class AdamUserServiceImpl implements IAdamUserService {

    @Value("${liquidnet.conf.des.key}")
    private String desKey;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private IAdamSysMessageService adamSysMessageService;

    @Autowired
    private IAdamSystemService adamSystemService;

    @Autowired
    private AdamUserMapper adamUserMapper;

    @Autowired
    private IAdamComInfoService adamComInfoService;

    @Autowired
    private IAdamUserAuthLogService adamUserAuthLogService;

    @Autowired
    private IAdamUserEmailService adamUserEmailService;

    @Autowired
    private IAdamUserPasswordService adamUserPasswordService;

    @Autowired
    private CustomIdGenerator customIdGenerator;

    @Autowired
    private AdamUserLoginBiz adamUserLoginBiz;

    @Autowired
    private IAdamAdminisProcessHisService adamAdminisProcessHisService;

    @Autowired
    private IAdamUserPermissionService adamUserPermissionService;

    @Autowired
    private IAdamWalletAccessService adamWalletAccessService;

    @Autowired
    private IAdamRoleService adamRoleService;

    @Autowired
    private IAdamRolePermissionService adamRolePermissionService;

    @Autowired
    private IAdamAccountWalletService adamAccountWalletService;

    @Autowired
    private IAdamComProfileService adamComProfileService;

    /**
     * 用户注册
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AdamResultDto userSignUp(AdamUserSignUpParam param) throws Exception {
        AdamResultDto dto = checkNecessaryField(param);
        if (!dto.isSuccess()) {
            return dto;
        }
        dto = adamUserPasswordService.checkPasswordFormat(param.getPassword());
        if (!dto.isSuccess()) {
            return dto;
        }
        dto = checkEmail(param.getEmail());
        if (!dto.isSuccess()) {
            return dto;
        }
        dto = checkCompanyExist(param.getEmail(), param.getCompanyName());
        if (!dto.isSuccess()) {
            return dto;
        }
        AdamUser adamUser = saveComAndUserInfo(param);
        String authUrl = adamUserEmailService.userEmailAuth(adamUser);
        dto.setData(authUrl);
        return dto;
    }

    /**
     * 必要字段非空校验
     */
    private AdamResultDto checkNecessaryField(AdamUserSignUpParam userSignUpParams) {
        if (StringUtils.isEmpty(userSignUpParams.getFirstName()) || StringUtils.isEmpty(userSignUpParams.getLastName())) {
            return AdamResultDto.failure(AdamErrorCode.NAME_LOST);
        }
        if (StringUtils.isEmpty(userSignUpParams.getJobTitle())) {
            return AdamResultDto.failure(AdamErrorCode.JOB_TITLE_LOST);
        }

        if (StringUtils.isEmpty(userSignUpParams.getEmail())) {
            return AdamResultDto.failure(AdamErrorCode.EMAIL_LOST);
        }

        if (StringUtils.isEmpty(userSignUpParams.getPassword())) {
            return AdamResultDto.failure(AdamErrorCode.PASSWORD_LOST);
        }

        if (StringUtils.isEmpty(userSignUpParams.getCompanyName())) {
            return AdamResultDto.failure(AdamErrorCode.COMPANY_LOST);
        }

        if (!EnumUtil.isExist(AdamComConstants.ComTypeEnum.values(), userSignUpParams.getComType())) {
            return AdamResultDto.failure(AdamErrorCode.ILLEGAL_PARAM);
        }

        AdamComConstants.ComTypeEnum enumFromString = EnumUtil.getEnumFromString(userSignUpParams.getComType(),
                AdamComConstants.ComTypeEnum.values());
        if (AdamComConstants.ComTypeEnum.OTHER.equals(enumFromString)
                && StringUtils.isEmpty(userSignUpParams.getComTypeOther())) {
            return AdamResultDto.failure(AdamErrorCode.ILLEGAL_PARAM);
        }

        return AdamResultDto.success();
    }

    /**
     * 必要字段非空校验
     */
    private boolean checkNecessaryFieldForInviteUser(AdamUserSignUpParam userSignUpParams, AdamResultDto dto) {
        if (StringUtils.isEmpty(userSignUpParams.getFirstName()) || StringUtils.isEmpty(userSignUpParams.getLastName())) {
            dto.setCode(AdamErrorCode.NAME_LOST.getCode());
            dto.setMessage(AdamErrorCode.NAME_LOST.getVal());
            return false;
        }
        if (StringUtils.isEmpty(userSignUpParams.getJobTitle())) {

            dto.setCode(AdamErrorCode.JOB_TITLE_LOST.getCode());
            dto.setMessage(AdamErrorCode.JOB_TITLE_LOST.getVal());
            return false;
        }

        if (StringUtils.isEmpty(userSignUpParams.getEmail())) {
            dto.setCode(AdamErrorCode.EMAIL_LOST.getCode());
            dto.setMessage(AdamErrorCode.EMAIL_LOST.getVal());
            return false;
        }

        return true;
    }

    // 校验邮箱是否注册
    private AdamResultDto checkEmail(String email) {
        AdamUser adamUser = getUserByEmail(email);
        if (null != adamUser) {
            AdamUserAuthLog userAuthLog = adamUserAuthLogService.getUserAuthLog(adamUser.getId(), AdamUserConstants.UserAuthTypeEnum.EMAIL);
            if (null != userAuthLog) {
                String authResult = userAuthLog.getAuthResult();
                if (authResult.equals(AdamUserConstants.UserAuthResultEnum.SUCCESS.name())) {
                    return AdamResultDto.failure(AdamErrorCode.EMAIL_HAS_REGIS);
                } else if (authResult.equals(AdamUserConstants.UserAuthResultEnum.PENDING.name())) {
                    return AdamResultDto.failure(AdamErrorCode.EMAIL_HAS_REGIS_VER);
                }
                return AdamResultDto.failure(AdamErrorCode.EMAIL_HAS_REGIS_VER);
            }
        }

        return EmailUtil.checkEmailFormat(email);
    }

    // 校验公司是否已注册
    private AdamResultDto checkCompanyExist(String email, String companyName) {
        int i = email.indexOf("@");
        String emailSuffix = email.substring(i);
        String comIdByUserEmailSuffix = adamUserMapper.getComIdByUserEmailSuffix(companyName, emailSuffix);
        if (!StringUtils.isEmpty(comIdByUserEmailSuffix)) {
            return AdamResultDto.failure(AdamErrorCode.COM_HAS_SIGNED);
        }
        return AdamResultDto.success();
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public AdamUser saveComAndUserInfo(AdamUserSignUpParam signUpParam) {
        AdamComInfoParam param = new AdamComInfoParam();
        Long aLong = customIdGenerator.nextId(param);
        param.setId(String.valueOf(aLong));
        param.setName(signUpParam.getCompanyName());
        param.setState(AdamComConstants.ComStateEnum.S1.getCode());
        param.setType(signUpParam.getComType());

        AdamComConstants.ComTypeEnum enumFromString = EnumUtil.getEnumFromString(signUpParam.getComType(),
                AdamComConstants.ComTypeEnum.values());
        if (AdamComConstants.ComTypeEnum.OTHER.equals(enumFromString)) {
            param.setTypeOther(signUpParam.getComTypeOther());
        }
        adamComInfoService.insert(param);

        AdamUser adamUser = new AdamUser();
        adamUser.setFirstName(signUpParam.getFirstName());
        adamUser.setLastName(signUpParam.getLastName());
        adamUser.setEmail(signUpParam.getEmail());
        adamUser.setStatus(AdamUserConstants.UserStatusEnum.ACTIVE.getCode());
        adamUser.setJobTitle(signUpParam.getJobTitle());
        adamUser.setPassword(adamUserPasswordService.getPasswordMD5(signUpParam.getPassword()));
        adamUser.setCreateTime(LocalDateTime.now());
        adamUser.setUpdateTime(LocalDateTime.now());
        adamUser.setComId(String.valueOf(aLong));
        adamUser.setRole(AdamUserConstants.UserRoleEnum.Administrator.name());

        adamUserMapper.insert(adamUser);

        adamUserPermissionService.assignPermissions(adamUser.getId(), adamUser.getComId(), adamUser.getRole());

        adamComProfileService.initial(adamUser.getComId());

        return adamUser;
    }

    @Override
    public AdamUser getUserByEmail(String email) {
        QueryWrapper<AdamUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("email", email);
        return adamUserMapper.selectOne(queryWrapper);
    }

    @Override
    public int updateById(AdamUser adamUser) {
        return adamUserMapper.updateById(adamUser);
    }

    public AdamUser getUserByPhone(String phoneCode, String phoneNumber) {
        LambdaQueryWrapper<AdamUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AdamUser::getPhoneCode, phoneCode);
        queryWrapper.eq(AdamUser::getPhoneNumber, phoneNumber);
        return adamUserMapper.selectOne(queryWrapper);
    }

    // 登录信息校验
    @Override
    public AdamResultDto<AdamUserSignInCheckRespDto> signInCheck(String email, String password) {
        AdamResultDto<AdamUserSignInCheckRespDto> resultDto = AdamResultDto.success();
        AdamUserSignInCheckRespDto adamUserSignInCheckRespDto = new AdamUserSignInCheckRespDto();

        AdamResultDto adamResultDto = EmailUtil.checkEmailFormat(email);
        if (!adamResultDto.isSuccess()) {
            return adamResultDto;
        }
        AdamUser adamUser = checkEmailExist(resultDto, email);
        if (!resultDto.isSuccess()) {
            return resultDto;
        }
        if (null == adamUser) {
            return AdamResultDto.failure(AdamErrorCode.EMAIL_NOT_REGIS);
        }
        // 校验用户登录密码
        resultDto = adamUserPasswordService.checkLoginPassword(adamUser, password);
        if (!resultDto.isSuccess()) {
            return resultDto;
        }
        AdamUserAuthLog userEmailAuthLog = adamUserAuthLogService.getUserAuthLog(adamUser.getId(),
                AdamUserConstants.UserAuthTypeEnum.EMAIL);
        if (null == userEmailAuthLog ||
                !AdamUserConstants.UserAuthResultEnum.SUCCESS.name().equalsIgnoreCase(userEmailAuthLog.getAuthResult())) {

            adamUserSignInCheckRespDto.setEmail(email);
            return AdamResultDto.success(adamUserSignInCheckRespDto);
        }
        adamUserSignInCheckRespDto.setNeedConfirmEmail(false);

        AdamUserAuthLog userSMSAuthLog = adamUserAuthLogService.getUserAuthLog(adamUser.getId(),
                AdamUserConstants.UserAuthTypeEnum.SMS_CODE);
        if (null == userSMSAuthLog ||
                !AdamUserConstants.UserAuthResultEnum.SUCCESS.name().equalsIgnoreCase(userSMSAuthLog.getAuthResult())) {
            adamUserSignInCheckRespDto.setEmail(email);

            return AdamResultDto.success(adamUserSignInCheckRespDto);
        }

        adamUserSignInCheckRespDto.setNeedBindPhoneNumber(false);
        String telephone = adamUser.getPhoneNumber();
        char[] chars = telephone.toCharArray();
        for (int i = 0; i < chars.length - 4; i++) {
            chars[i] = '*';
        }
        adamUserSignInCheckRespDto.setPhone(new String(chars));
        sendSMSConfirmCode(adamUser, adamUser.getPhoneCode(), adamUser.getPhoneNumber());

        resultDto.setData(adamUserSignInCheckRespDto);
        return resultDto;
    }

    private void sendSMSConfirmCode(AdamUser adamUser, String phoneCode, String phoneNumber) {
        String confirmCode = adamSystemService.generateVerifyCode();

        // userId 作为key,电话代码 + 电话号码 + 验证码为value 以便在重新发送验证码时将就验证码置位失效 同时在绑定手机号时无需前端传回对应参数
        String redisKey = AdamRedisConstants.USER_CONFIRM_SMS_CODE_KEY + adamUser.getId();
        String redisValue = phoneCode + "_" + phoneNumber + "_" + confirmCode;
        redisUtil.set(redisKey, redisValue, TimeUnit.MINUTES.toSeconds(2));

        log.info("[SEND SMS CODE]; userId : {}, code : {}", adamUser.getId(), confirmCode);

        // todo-lichen 未完成,短信模板内容需要更新
        String message = "短信验证码" + confirmCode;
        adamSysMessageService.asyncSendMessage(message, phoneNumber);
    }

    private AdamUser checkEmailExist(AdamResultDto dto, String email) {
        AdamUser adamUser = getUserByEmail(email);
        if (null == adamUser) {
            dto.setCode(AdamErrorCode.EMAIL_NOT_REGIS.getCode());
            dto.setMessage(AdamErrorCode.EMAIL_NOT_REGIS.getVal());
        }
        return adamUser;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public AdamResultDto bindUserPhoneAndSendSMSCode(String email, String phoneCode, String phoneNumber) {
        AdamResultDto dto = new AdamResultDto();
        AdamUser adamUser = checkEmailExist(dto, email);
        if (!dto.isSuccess()) {
            return dto;
        }

        AdamUser exist = getUserByPhone(phoneCode, phoneNumber);
        if (null != exist) {
            return AdamResultDto.failure(AdamErrorCode.PHONE_NUMBER_EXIST);
        }

        AdamUserAuthLog smsAuthLog = adamUserAuthLogService.getUserAuthLog(adamUser.getId(),
                AdamUserConstants.UserAuthTypeEnum.SMS_CODE);
        if (null != smsAuthLog
                && AdamUserConstants.UserAuthResultEnum.SUCCESS.name().equalsIgnoreCase(smsAuthLog.getAuthResult())) {
            return AdamResultDto.failure(AdamErrorCode.PHONE_AUTH_PASSED);
        }

        sendSMSConfirmCode(adamUser, phoneCode, phoneNumber);

        if (null == smsAuthLog) {
            smsAuthLog = new AdamUserAuthLog();
            smsAuthLog.setUserId(adamUser.getId());
            smsAuthLog.setAuthType(AdamUserConstants.UserAuthTypeEnum.SMS_CODE.name());
            smsAuthLog.setAuthResult(AdamUserConstants.UserAuthResultEnum.PENDING.name());
            adamUserAuthLogService.insert(smsAuthLog);
        } else {
            smsAuthLog.setAuthResult(AdamUserConstants.UserAuthResultEnum.PENDING.name());
            adamUserAuthLogService.updateById(smsAuthLog);
        }

        return dto;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public AdamResultDto<AdamUserSignInRespDto> confirmSMSCode(String email, String smsCode, String ip) {
        AdamResultDto<AdamUserSignInRespDto> dto = new AdamResultDto();
        AdamUserSignInRespDto adamUserSignInRespDto = new AdamUserSignInRespDto();
        dto.setData(adamUserSignInRespDto);
        if (StringUtils.isEmpty(email) || StringUtils.isEmpty(smsCode)) {
            return AdamResultDto.failure(AdamErrorCode.PARAM_ERROR);
        }

        AdamUser adamUser = getUserByEmail(email);
        if (null == adamUser) {
            return AdamResultDto.failure(AdamErrorCode.EMAIL_NOT_REGIS);
        }

        String redisKey = AdamRedisConstants.USER_CONFIRM_SMS_CODE_KEY + adamUser.getId();

        String redisValue = (String) redisUtil.get(redisKey);

        if (StringUtils.isEmpty(redisValue)) {
            return AdamResultDto.failure(AdamErrorCode.WRONG_VERIF_CODE);
        }

        String[] split = redisValue.split("_");
        if (split.length != 3) {
            return AdamResultDto.failure(AdamErrorCode.ILLEGAL_PARAM);
        }
        // 校验短信验证码
        if (!smsCode.equalsIgnoreCase(split[2])) {
            return AdamResultDto.failure(AdamErrorCode.ILLEGAL_VERIF_CODE);
        }

        redisUtil.del(redisKey);

        // sign in success
        if (StringUtils.isEmpty(adamUser.getPhoneNumber())) {
            adamUser.setPhoneCode(split[0]);
            adamUser.setPhoneNumber(split[1]);
            adamUserMapper.updateById(adamUser);
        }

        List<String> permitList = Lists.newArrayList();
        try {
            permitList = adamUserPermissionService.queryPermissionKeyByUserId(adamUser.getId());
        } catch (Exception e) {
            log.error("err:User login get function permissions failed:userId:{}", adamUser.getId(), e);
        }

        String token = saveAndGetUserToken(adamUser.getId(), ip, permitList);
        adamUserSignInRespDto.setUserId(adamUser.getId());
        adamUserSignInRespDto.setToken(token);
        adamUserSignInRespDto.setPermissionList(permitList);
        processUserSMSAuth(adamUser);
        return dto;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void processUserSMSAuth(AdamUser adamUser) {
        AdamUserAuthLog userAuthLog = adamUserAuthLogService.getUserAuthLog(adamUser.getId(),
                AdamUserConstants.UserAuthTypeEnum.SMS_CODE);
        if (null == userAuthLog) {
            userAuthLog = new AdamUserAuthLog();
            userAuthLog.setAuthType(AdamUserConstants.UserAuthTypeEnum.SMS_CODE.name());
            userAuthLog.setUserId(adamUser.getId());
            userAuthLog.setAuthResult(AdamUserConstants.UserAuthResultEnum.SUCCESS.name());
            adamUserAuthLogService.insert(userAuthLog);
        } else {
            userAuthLog.setAuthResult(AdamUserConstants.UserAuthResultEnum.SUCCESS.name());
            adamUserAuthLogService.updateById(userAuthLog);
        }
    }

    private String saveAndGetUserToken(String userId, String ip, List<String> permitList) {

        String token = "";
        try {
            DESUtils desUtils = new DESUtils(desKey);
            token = desUtils.encrypt(ip + "-lk-token_" + userId);
        } catch (Exception e) {
            log.error("get des utils error at " + LocalDateTime.now());
            log.error(e.getMessage(), e);
        }

        //设置用户登录缓存
        adamUserLoginBiz.userLoginCacheSet(userId, token, ip, permitList);
        return token;
    }

    @Override
    public AdamUser selectById(String userId) {
        return adamUserMapper.selectById(userId);
    }

    @Override
    public AdamUser selectOne(AdamUser adamUser) {
        LambdaQueryWrapper<AdamUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(!StringUtils.isEmpty(adamUser.getId()), AdamUser::getId, adamUser.getId())
                .eq(!StringUtils.isEmpty(adamUser.getComId()), AdamUser::getComId, adamUser.getComId());
        return adamUserMapper.selectOne(queryWrapper);
    }

    @Override
    public Page<AdamUser> queryPageList(int pageNum, int pageSize, AdamUserQuery queryParam) {
        QueryWrapper<AdamUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(!StringUtils.isEmpty(queryParam.getRole()),
                        AdamUser::getRole, queryParam.getRole())
                .eq(!StringUtils.isEmpty(queryParam.getEmail()),
                        AdamUser::getEmail, queryParam.getEmail())
                .eq(!StringUtils.isEmpty(queryParam.getUserPhoneNumber()),
                        AdamUser::getPhoneNumber, queryParam.getUserPhoneNumber())
                .ge(!StringUtils.isEmpty(queryParam.getCreatedTimeStart()),
                        AdamUser::getCreateTime, queryParam.getCreatedTimeStart())
                .le(!StringUtils.isEmpty(queryParam.getCreatedTimeEnd()),
                        AdamUser::getCreateTime, queryParam.getCreatedTimeEnd())
                .orderByAsc(AdamUser::getCreateTime);

        return adamUserMapper.selectPage(new Page<AdamUser>(pageNum, pageSize), queryWrapper);
    }

    @Override
    public List<AdamUser> queryListForAdminis(String comId) {
        LambdaQueryWrapper<AdamUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AdamUser::getComId, comId);
        return adamUserMapper.selectList(queryWrapper);
    }

    @Override
    public Page<AdamUser> queryPageListForAdminis(AdamCommonQuery query) {
        LambdaQueryWrapper<AdamUser> queryWrapper = new LambdaQueryWrapper<>();

        // Match contains: User First Name, User Last Name, Company Email (Login ID), User Role, Status. Incomplete search allowed
        String search = query.getSearch();
        String startTime = query.getStartTime();
        String endTime = query.getEndTime();
        Integer pageNo = query.getPageNo();
        Integer pageSize = query.getPageSize();

        String periodsType = query.getPeriodsType();
        boolean isCreateTime = "1".equals(periodsType);
        boolean isUpdateTime = "2".equals(periodsType);

        boolean startTimeNotEmpty = !StringUtils.isEmpty(startTime);
        boolean endTimeNotEmpty = !StringUtils.isEmpty(endTime);

        if (!StringUtils.isEmpty(search)) {
            queryWrapper.and(i -> i.like(AdamUser::getFirstName, search)
                    .or().like(AdamUser::getLastName, search)
                    .or().like(AdamUser::getEmail, search)
                    .or().like(AdamUser::getRole, search)
                    .or().like(AdamUser::getStatus, search)
            );
        }
        if (!StringUtils.isEmpty(query.getComId())) {
            queryWrapper.eq(AdamUser::getComId, query.getComId());
        }
        if (isCreateTime && startTimeNotEmpty) {
            queryWrapper.eq(AdamUser::getCreateTime, startTime);
        }
        if (isCreateTime && endTimeNotEmpty) {
            queryWrapper.le(AdamUser::getCreateTime, endTime);
        }
        if (isUpdateTime && startTimeNotEmpty) {
            queryWrapper.ge(AdamUser::getUpdateTime, startTime);
        }
        if (isUpdateTime && endTimeNotEmpty) {
            queryWrapper.le(AdamUser::getUpdateTime, endTime);
        }
        queryWrapper.orderByDesc(AdamUser::getUpdateTime).orderByAsc(AdamUser::getFirstName);

        return adamUserMapper.selectPage(new Page<>(null == pageNo ? 1 : pageNo, null == pageSize ? 10 : pageSize), queryWrapper);
    }

    @Override
    public AdamUserProfileVo queryByUserId(String uid, String hisTag, int pageNo, int pageSize) {
        // Basic profile
        AdamUser adamUser = adamUserMapper.selectById(uid);
        if (adamUser == null) {
            return null;
        }
        AdamUserProfileVo userProfileVo = AdamUserProfileVo.getNew().copy(adamUser);

        // Function permission
        userProfileVo.setUserPermissions(adamUserPermissionService.queryPermissionByUserId(uid));

        // Access to wallets
        userProfileVo.setUserAllowedWallets(adamWalletAccessService.queryForUserProfileAdminis(adamUser.getComId(), uid));

        // Process history
        boolean isAdmin = AdamUserConstants.UserRoleEnum.Administrator.name().equalsIgnoreCase(adamUser.getRole());
        Page<AdamAdminisProcessHisVo> processHisVoPage = adamAdminisProcessHisService.queryPageListForProcessHis(hisTag, isAdmin ? null : uid, pageNo, pageSize);
        userProfileVo.setProcessHis(processHisVoPage.getRecords());

        // Role permissions
        userProfileVo.setRoleMap(adamRoleService.query());
        userProfileVo.setRolePermissionsMap(adamRolePermissionService.queryRolePermissions());

        return userProfileVo;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ResponseDto<?> inviteUserDo(AdamCurrentUser currentUser, String roleName, AdamAdminisUserParam parameter) throws Exception {
        // Basic profile
        AdamResultDto<?> dto = checkEmail(parameter.getCompanyEmail());
        if (!dto.isSuccess()) {
            return AdamResultDto.getResponseDto(dto);
        }
        String comId = currentUser.getComId();
        AdamUser adamUser = new AdamUser();
        adamUser.setFirstName(parameter.getFirstName());
        adamUser.setLastName(parameter.getLastName());
        adamUser.setEmail(parameter.getCompanyEmail());
        adamUser.setJobTitle(parameter.getJobTitle());
        adamUser.setCreateTime(LocalDateTime.now());
        adamUser.setUpdateTime(adamUser.getCreateTime());
        adamUser.setComId(comId);
        adamUser.setRole(roleName);
        adamUser.setStatus(AdamUserConstants.UserStatusEnum.INVITED.getCode());
        adamUserMapper.insert(adamUser);

        parameter.setUserId(adamUser.getId());
        ResponseDto<?> responseDto = this.processPermissions(comId, parameter, roleName);
        if (!responseDto.isSuccess()) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return responseDto;
        }

        // Record process history
        adamAdminisProcessHisService.record(AdamAdminisConstants.ProcessHisEnum.USER_ADMINIS_CREATED, null,
                new String[]{comId, parameter.getUserId(), currentUser.getId(), currentUser.getName()});

        // Send invite email
        String mailMsg = adamUserEmailService.userEmailAuthForInvite(adamUser, true);

        return ResponseDto.success(mailMsg);
    }

    /**
     * `Function Permissions` and `Access to Wallets`
     */
    private ResponseDto<?> processPermissions(String comId, AdamAdminisUserParam parameter, String roleName) {
        // User permissions
        List<String> userPermissionList = parameter.getUserPermissionList();
        List<AdamRolePermission> rolePermissionList = adamRolePermissionService.queryRolePermissions(parameter.getRoleId());
        List<AdamRolePermission> assignRolePermList = rolePermissionList.stream().filter(r -> !r.getExecutable().equals("2")
                && userPermissionList.contains(r.getAuthId())).collect(Collectors.toList());
        List<AdamRolePermission> notAssignDefaultRolePermList = rolePermissionList.stream().filter(r -> r.getExecutable().equals("1")
                && r.getEditable().equals("2") && !userPermissionList.contains(r.getAuthId())).collect(Collectors.toList());
        if (userPermissionList.size() > assignRolePermList.size() || !CollectionUtils.isEmpty(notAssignDefaultRolePermList)) {
            log.warn("Invalid auth ID:[{}]", JsonUtils.toJson(userPermissionList));
            return ResponseDto.failure(AdamErrorCode.ADAM001_ILLEGAL_PARAM.getCode(), AdamErrorCode.ADAM001_ILLEGAL_PARAM.getVal().concat(":permissions"));
        }
        if (!adamUserPermissionService.savePermissions(assignRolePermList, parameter.getUserId(), comId, parameter.getRoleId())) {
            log.warn("save user permissions failed:[{}]", JsonUtils.toJson(userPermissionList));
            return ResponseDto.failure(ErrorCode.RESPONSE_ERROE_BIZ);
        }

        // User access wallets
        List<String> userAllowedWalletList = parameter.getUserAllowedWalletList();
        AdamAccountWalletParam accountWalletParam = new AdamAccountWalletParam();
        accountWalletParam.setComId(comId);
        accountWalletParam.setState(AdamAccountConstants.StateEnum.S2.getCode());
        List<AdamAccountWallet> walletList = adamAccountWalletService.selectList(accountWalletParam);
        List<AdamAccountWallet> assignWalletList = walletList.stream().filter(r -> userAllowedWalletList.contains(r.getFinWalletNo())).collect(Collectors.toList());
        if (userAllowedWalletList.size() > assignWalletList.size()
                || (AdamUserConstants.UserRoleEnum.Administrator.name().equals(roleName) && userAllowedWalletList.size() != walletList.size())) {
            log.warn("Invalid wallet no:[{}]", JsonUtils.toJson(userAllowedWalletList));
            return ResponseDto.failure(AdamErrorCode.ADAM001_ILLEGAL_PARAM.getCode(), AdamErrorCode.ADAM001_ILLEGAL_PARAM.getVal().concat(":access wallet no"));
        }
        if (!adamWalletAccessService.saveWalletAccess(userAllowedWalletList, comId, parameter.getUserId())) {
            log.warn("save access wallet no failed:[{}]", JsonUtils.toJson(userAllowedWalletList));
            return ResponseDto.failure(ErrorCode.RESPONSE_ERROE_BIZ);
        }
        return ResponseDto.success();
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ResponseDto updateInviteDo(AdamCurrentUser currentUser, String roleName, AdamAdminisUserParam parameter) throws Exception {
        ResponseDto responseDto = null;
        // Basic profile
        AdamUser adamUser = getUserByEmail(parameter.getCompanyEmail());
        if (adamUser.getStatus().equals(AdamUserConstants.UserStatusEnum.REMOVED.getCode())) {
            return ResponseDto.failure(AdamErrorCode.ADAM_ADMINISTRATION_009003.getCode(), AdamErrorCode.ADAM_ADMINISTRATION_009003.getVal());
        }
        boolean isInvited = adamUser.getStatus().equals(AdamUserConstants.UserStatusEnum.INVITED.getCode());
        String comId = currentUser.getComId();
        if (isInvited) {
            AdamResultDto<?> dto = new AdamResultDto<>();
            if (!EmailUtil.checkEmailFormat(parameter.getCompanyEmail()).isSuccess()) {
                return AdamResultDto.getResponseDto(dto);
            }
            AdamUser updateUser = new AdamUser();
            updateUser.setId(parameter.getUserId());
            updateUser.setFirstName(parameter.getFirstName());
            updateUser.setLastName(parameter.getLastName());
            updateUser.setEmail(parameter.getCompanyEmail());
            updateUser.setJobTitle(parameter.getJobTitle());
            updateUser.setUpdateTime(updateUser.getCreateTime());
            updateUser.setRole(roleName);
            adamUserMapper.updateById(updateUser);

            responseDto = this.processPermissions(comId, parameter, roleName);
            if (!responseDto.isSuccess()) {
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return responseDto;
            }

            // Send invite email
            String mailMsg = adamUserEmailService.userEmailAuthForInvite(updateUser, false);

            responseDto.setData(mailMsg);
        } else {// User activated
            responseDto = this.processPermissions(comId, parameter, roleName);
            if (!responseDto.isSuccess()) {
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return responseDto;
            }
        }
        redisUtil.hdel(adamUserLoginBiz.getUserLoginKey(parameter.getUserId()), AdamRedisConstants.USER_LOGIN_ITEM_PERMIT);

        // Record process history
        adamAdminisProcessHisService.record(AdamAdminisConstants.ProcessHisEnum.USER_ADMINIS_UPDATED, null,
                new String[]{comId, parameter.getUserId(), currentUser.getId(), currentUser.getName()});

        return responseDto;
    }

    @Override
    public int updateStatus(String userId, AdamUserConstants.UserStatusEnum statusEnum) {
        AdamUser updateInfo = new AdamUser();
        updateInfo.setId(userId);
        updateInfo.setStatus(statusEnum.getCode());
        return adamUserMapper.updateById(updateInfo);
    }

}
