package com.dji.sample.manage.service.impl;

import com.auth0.jwt.JWT;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dji.sample.common.model.CustomClaim;
import com.dji.sample.common.util.JwtUtil;
import com.dji.sample.common.util.SecurityUtils;
import com.dji.sample.component.mqtt.config.MqttPropertyConfiguration;
import com.dji.sample.component.oss.model.OssConfiguration;
import com.dji.sample.component.oss.service.impl.OssServiceContext;
import com.dji.sample.component.redis.RedisConst;
import com.dji.sample.component.redis.RedisOpsUtils;
import com.dji.sample.manage.dao.IUserMapper;
import com.dji.sample.manage.model.dto.*;
import com.dji.sample.manage.model.entity.OrgEntity;
import com.dji.sample.manage.model.entity.UserEntity;
import com.dji.sample.manage.model.entity.UserOrgEntity;
import com.dji.sample.manage.model.entity.WorkspaceEntity;
import com.dji.sample.manage.model.enums.RoleTypeEnum;
import com.dji.sample.manage.model.enums.UserTypeEnum;
import com.dji.sample.manage.model.param.ChangePasswordParam;
import com.dji.sample.manage.model.param.searchParam.LoginLogSearchParam;
import com.dji.sample.manage.model.param.searchParam.UserSearchParam;
import com.dji.sample.manage.model.password.PasswordRuleContext;
import com.dji.sample.manage.model.password.PasswordRuleResult;
import com.dji.sample.manage.service.*;
import com.dji.sample.manage.service.password.PasswordRuleChain;
import com.dji.sdk.common.HttpResultResponse;
import com.dji.sdk.common.Pagination;
import com.dji.sdk.common.PaginationData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.net.URL;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;

import static com.dji.sample.common.util.SecurityUtils.*;

@Service
@Transactional
public class UserServiceImpl extends ServiceImpl<IUserMapper, UserEntity> implements IUserService {

    @Autowired
    private IUserMapper mapper;

    @Autowired
    private MqttPropertyConfiguration mqttPropertyConfiguration;

    @Autowired
    private IOrgService orgService;

    @Autowired
    private ILoginLogService loginLogService;

    @Autowired
    private IUserOrgService userOrgService;

    @Autowired
    private IWorkspaceService workspaceService;

    @Autowired
    private OssServiceContext ossService;

    @Autowired
    private IUserPasswordHistoryService userPasswordHistoryService;

    @Autowired
    private PasswordRuleChain passwordRuleChain;


    /**
     * 登录尝试次数
     */
    public static final String LOGIN_TRY_COUNT = "LOGIN_TRY_COUNT";

    private final static String LOGIN_USER_NOT_PASS = "username or password invalid";

    private final static String LOGIN_USER_NAME_NOT_EXIST = "user name does not exist";

    private final static String ACCOUNT_TYPE_NOT_MATCH = "Account type does not match";

    private final static String INVALID_PASSWORD = "Invalid password";

    private final static String INVALID_WORKSPACE = "Invalid workspace";

    /**
     * 最大尝试登录次数
     */
    private final static Integer LOGIN_TRY_COUNT_MAX = 5;

    /**
     * 最大尝试登录次数
     * 第二阶段
     */
    private final static Integer LOGIN_TRY_COUNT_MAX_SECOND = 10;

    /**
     * 登录尝试的锁
     */
    private final static String LOGIN_TRY_LOCK = "LOGIN_TRY_LOCK";


    private final static String LOGIN_TRY_COUNT_MAX_EXCEEDED = "login try count max exceeded, user locked";

    private final static long HOUR_SECONDS = 60 * 60;

    private final static long DATE_SECONDS = 24 * 60 * 60;

    /**
     * Password expiry in days (90 days = ~3 months, max 180 = ~6 months).
     * Set to 90 days to comply with the 3–6 month policy.
     */
    private static final long PASSWORD_EXPIRY_DAYS = 90;

    private static final long PASSWORD_EXPIRY_MILLIS = PASSWORD_EXPIRY_DAYS * 24 * 60 * 60 * 1000L;

    private static final int PASSWORD_HISTORY_COUNT = 8;

    @Override
    public HttpResultResponse getUserByUsername(String username, String workspaceId) {

        UserEntity userEntity = this.getUserByUsername(username);
        if (userEntity == null) {
            return new HttpResultResponse()
                    .setCode(HttpStatus.UNAUTHORIZED.value())
                    .setMessage("invalid username");
        }

        UserDTO user = this.entityConvertToDTO(userEntity);
        user.setWorkspaceId(workspaceId);

        return HttpResultResponse.success(user);
    }

    @Override
    public PaginationData<LoginLogDTO> getUserLoginLog(LoginLogSearchParam param, String userId, Integer roleType, int page, int pageSize) {

        if (param == null) {
            param = new LoginLogSearchParam();
        }

        if (roleType == null || roleType != RoleTypeEnum.ADMIN.getVal()) {
            param.setUserId(userId);
        }

        return loginLogService.getLoginLogList(param, page, pageSize);

    }

    /**
     * 更新用户尝试登录次数
     *
     * @param user            用户
     * @param loginTryCount 登录尝试次数
     */
    private void updateUserLoginTryCount(UserEntity user, Integer loginTryCount) {

        LambdaUpdateWrapper<UserEntity> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(UserEntity::getLoginTryCount, loginTryCount);

        updateWrapper.eq(UserEntity::getId, user.getId());
        mapper.update(null, updateWrapper);

    }

    /**
     * 解锁用户
     *
     * @param userEntity 实体
     */
    public void unlockUser(UserEntity userEntity) {

        String username = userEntity.getUsername();
        updateUserLoginTryCount(userEntity, 0);

        RedisOpsUtils.del(LOGIN_TRY_COUNT + RedisConst.DELIMITER + username);
        RedisOpsUtils.del(LOGIN_TRY_LOCK + RedisConst.DELIMITER + username);

    }

    /**
     * 处理登录密码错误的问题
     */
    private void handleLoginPasswordFail(String ipAddress, String userAgent, UserEntity userEntity) {

        Integer loginTryCount = userEntity.getLoginTryCount();
        if (loginTryCount == null) {
            loginTryCount = 0;
        }
        loginTryCount++;

        // 更新用户尝试登录次数
        updateUserLoginTryCount(userEntity, loginTryCount);

        // 把登录尝试次数放在Redis中
        addLoginTryCountToRedis(userEntity.getUsername());

        // 增加登录日志
        loginLogService.addLoginLog(userEntity, false, INVALID_PASSWORD, ipAddress, userAgent);
    }

    private void addLoginTryCountToRedis(String username) {

        String redisKey = LOGIN_TRY_COUNT + RedisConst.DELIMITER + username;

        Integer loginTryCount = RedisOpsUtils.getInt(redisKey);
        if (loginTryCount == null) {
            loginTryCount = 0;
        }
        loginTryCount++;
        // 把尝试次数加入到redis中
        RedisOpsUtils.setWithExpire(redisKey, loginTryCount, DATE_SECONDS);

        // 超过尝试登录次数的话，用户锁定
        if (loginTryCount.equals(LOGIN_TRY_COUNT_MAX)) {
            RedisOpsUtils.setWithExpire(LOGIN_TRY_LOCK + RedisConst.DELIMITER + username, System.currentTimeMillis(), HOUR_SECONDS);
        }

        // 超过尝试登录次数的第二阶段的话，用户锁定更长的时间
        if (loginTryCount >= LOGIN_TRY_COUNT_MAX_SECOND) {
            RedisOpsUtils.setWithExpire(LOGIN_TRY_LOCK + RedisConst.DELIMITER + username, System.currentTimeMillis(), DATE_SECONDS);
        }

    }


    /**
     * 检查当前的IP，尝试登录的次数是否超过
     *
     * @param username 用户名
     * @return 结果
     */
    private boolean checkLoginTryCountLock(String username) {
        String redisKey = LOGIN_TRY_LOCK + RedisConst.DELIMITER + username;
        return !RedisOpsUtils.checkExist(redisKey);
    }

    /**
     * Build a {@link PasswordRuleContext} from a {@link UserEntity} for use in password validation.
     */
    private PasswordRuleContext buildContext(UserEntity user) {
        if (user == null) {
            return null;
        }
        return PasswordRuleContext.builder()
                .username(user.getUsername())
                .phoneNumber(user.getPhoneNumber())
                .build();
    }

    /**
     * Apply a new password for the given user: encode, persist, update lastChangePassword,
     * and record the password in history.
     */
    private void applyNewPassword(UserEntity userEntity, String rawPassword) {
        String encoded = SecurityUtils.encryptPassword(rawPassword);
        LambdaUpdateWrapper<UserEntity> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(UserEntity::getPassword, encoded)
                .set(UserEntity::getLastChangePassword, System.currentTimeMillis())
                .set(UserEntity::getNewAdd, false)
                .eq(UserEntity::getUserId, userEntity.getUserId());
        mapper.update(null, updateWrapper);

        userPasswordHistoryService.savePasswordHistory(
                userEntity.getUserId(), userEntity.getWorkspaceId(), encoded, PASSWORD_HISTORY_COUNT);
    }

    @Override
    public HttpResultResponse<Object> changePassword(String userId, ChangePasswordParam param) {
        if (!org.springframework.util.StringUtils.hasText(param.getNewPassword())) {
            return HttpResultResponse.error("New password must not be empty");
        }

        UserEntity userEntity = getUserByUserId(userId);
        if (userEntity == null) {
            return HttpResultResponse.error("User not found");
        }

        PasswordRuleResult strengthCheck = passwordRuleChain.validate(param.getNewPassword(), buildContext(userEntity));
        if (!strengthCheck.isValid()) {
            return HttpResultResponse.error(strengthCheck.getReason());
        }

        // Verify old password
        if (!SecurityUtils.matchesPassword(param.getOldPassword(), userEntity.getPassword())) {
            return HttpResultResponse.error("Current password is incorrect");
        }

        // Disallow reuse of last PASSWORD_HISTORY_COUNT passwords
//        if (userPasswordHistoryService.isPasswordRecentlyUsed(userId, param.getNewPassword(), PASSWORD_HISTORY_COUNT)) {
//            return HttpResultResponse.error(
//                    "New password cannot be the same as any of the last " + PASSWORD_HISTORY_COUNT + " passwords");
//        }

        applyNewPassword(userEntity, param.getNewPassword());
        return HttpResultResponse.success();
    }

    @Override
    public HttpResultResponse<Object> resetPassword(String targetUserId, ChangePasswordParam param) {
        // Only admins may reset other users' passwords
        aboveAdminRoleAndThrowError();

        if (!org.springframework.util.StringUtils.hasText(param.getNewPassword())) {
            return HttpResultResponse.error("New password must not be empty");
        }

        UserEntity userEntity = getUserByUserId(targetUserId);
        if (userEntity == null) {
            return HttpResultResponse.error("User not found");
        }

        PasswordRuleResult strengthCheck = passwordRuleChain.validate(param.getNewPassword(), buildContext(userEntity));
        if (!strengthCheck.isValid()) {
            return HttpResultResponse.error(strengthCheck.getReason());
        }

        // Disallow reuse of last PASSWORD_HISTORY_COUNT passwords
        if (userPasswordHistoryService.isPasswordRecentlyUsed(targetUserId, param.getNewPassword(), PASSWORD_HISTORY_COUNT)) {
            return HttpResultResponse.error(
                    "New password cannot be the same as any of the last " + PASSWORD_HISTORY_COUNT + " passwords");
        }

        applyNewPassword(userEntity, param.getNewPassword());
        return HttpResultResponse.success();
    }

    @Override
    public HttpResultResponse<Object> changePasswordByAdmin(String targetUserId, ChangePasswordParam param) {
        // Only admins may change other users' passwords
        aboveAdminRoleAndThrowError();

        if (!org.springframework.util.StringUtils.hasText(targetUserId)) {
            return HttpResultResponse.error("User ID must not be empty");
        }

        if (!org.springframework.util.StringUtils.hasText(param.getNewPassword())) {
            return HttpResultResponse.error("New password must not be empty");
        }

        UserEntity userEntity = getUserByUserId(targetUserId);
        if (userEntity == null) {
            return HttpResultResponse.error("User not found");
        }

        PasswordRuleResult strengthCheck = passwordRuleChain.validate(param.getNewPassword(), buildContext(userEntity));
        if (!strengthCheck.isValid()) {
            return HttpResultResponse.error(strengthCheck.getReason());
        }

        // Disallow reuse of last PASSWORD_HISTORY_COUNT passwords
        if (userPasswordHistoryService.isPasswordRecentlyUsed(targetUserId, param.getNewPassword(), PASSWORD_HISTORY_COUNT)) {
            return HttpResultResponse.error(
                    "New password cannot be the same as any of the last " + PASSWORD_HISTORY_COUNT + " passwords");
        }

        applyNewPassword(userEntity, param.getNewPassword());
        return HttpResultResponse.success();
    }

    /**
     * is password expired
     *
     * @param userEntity User Entity
     * @return result
     */
    private boolean isPasswordExpired(UserEntity userEntity) {
        // New-add users are forced to change password at first login; treat as expired.
        if (Boolean.TRUE.equals(userEntity.getNewAdd())) {
            return true;
        }
        Long lastChange = userEntity.getLastChangePassword();
        if (lastChange == null) {
            // Never changed – treat as expired so user is prompted.
            return false;
        }
        return (System.currentTimeMillis() - lastChange) > PASSWORD_EXPIRY_MILLIS;
    }

    @Override
    public boolean isPasswordExpired(String userId) {
        UserEntity userEntity = getUserByUserId(userId);
        if (userEntity == null) {
            return false;
        }

        return isPasswordExpired(userEntity);

    }

    /**
     * Verify the username and password to log in.
     * @param loginDTO
     * @return
     */
    @Override
    public HttpResultResponse userLogin(UserLoginDTO loginDTO) {

        // 登录用户的IP地址
        String ipAddress = loginDTO.getIpAddress();
        String username = loginDTO.getUsername();

        // 检查登录尝试是否被锁定
        if (!checkLoginTryCountLock(username)) {
            // 超过登录尝试的次数
            return HttpResultResponse.unauthorized(LOGIN_TRY_COUNT_MAX_EXCEEDED);
        }

        String userAgent = loginDTO.getUserAgent();

        Integer flag = loginDTO.getFlag();
        String password = loginDTO.getPassword();
        String orgId = loginDTO.getOrgId();
        String orgName = loginDTO.getOrgName();
        if (!StringUtils.hasText(loginDTO.getOrgId()) && !StringUtils.hasText(loginDTO.getOrgName())) {
            orgId = "geoSys";
        }
        // check user
        UserEntity userEntity = this.getUserByUsername(username);
        if (userEntity == null) {
            loginLogService.addLoginLog(username, false, LOGIN_USER_NAME_NOT_EXIST, ipAddress, userAgent);
            addLoginTryCountToRedis(username);

            return HttpResultResponse.unauthorized(LOGIN_USER_NOT_PASS);
        }
        // 修改逻辑 跳过web判定
        if (flag != UserTypeEnum.WEB.getVal()) {
            if (userEntity.getRoleType() == null || userEntity.getRoleType() != RoleTypeEnum.SYS_ADMIN.getVal() ) {
                if (flag.intValue() != userEntity.getUserType().intValue()) {
                    loginLogService.addLoginLog(userEntity, false, ACCOUNT_TYPE_NOT_MATCH, ipAddress, userAgent);
                    addLoginTryCountToRedis(username);
                    return HttpResultResponse.unauthorized(ACCOUNT_TYPE_NOT_MATCH);
                }
            }
        }
        // 密码加密验证
        // if (!password.equals(userEntity.getPassword())) {
        if (!SecurityUtils.matchesPassword(password, userEntity.getPassword())) {

            // 处理密码登录失败的问题
            handleLoginPasswordFail(ipAddress, userAgent, userEntity);

            return HttpResultResponse.unauthorized(LOGIN_USER_NOT_PASS);
        }

        // 用户登录成功后，解除尝试限制
        unlockUser(userEntity);

        Optional<WorkspaceDTO> workspaceOpt = workspaceService.getWorkspaceByWorkspaceId(userEntity.getWorkspaceId());
        if (workspaceOpt.isEmpty()) {
            loginLogService.addLoginLog(userEntity, false, INVALID_WORKSPACE, ipAddress, userAgent);
            return HttpResultResponse.unauthorized(INVALID_WORKSPACE);
        }

        // 查询 org
        LambdaQueryWrapper<OrgEntity> orgQueryWrapper = new LambdaQueryWrapper<>();
        orgQueryWrapper.eq(OrgEntity::getWorkspaceId, userEntity.getWorkspaceId());
        if (StringUtils.hasText(orgId)) {
            orgQueryWrapper.eq(OrgEntity::getOrgId, orgId);
        } else if (StringUtils.hasText(orgName)) {
            orgQueryWrapper.eq(OrgEntity::getOrgName, orgName);
        } else {
            loginLogService.addLoginLog(userEntity, false, "Invalid org info", ipAddress, userAgent);
            return HttpResultResponse.unauthorized("invalid org info");
        }
        List<OrgEntity> orgEntityList = orgService.list(orgQueryWrapper);
        if (CollectionUtils.isEmpty(orgEntityList)) {
            loginLogService.addLoginLog(userEntity, false, "Invalid org id", ipAddress, userAgent);
            return HttpResultResponse.unauthorized("invalid org id");
        }
        OrgEntity orgEntity = orgEntityList.get(0);
        // 查询权限
        Integer roleType;
        // 系统管理员
        if (userEntity.getRoleType() != null && userEntity.getRoleType() == RoleTypeEnum.SYS_ADMIN.getVal()) {
            roleType = userEntity.getRoleType();
        } else {
            // 团队权限
            LambdaQueryWrapper<UserOrgEntity> userOrgWrapper = new LambdaQueryWrapper<>();
            userOrgWrapper.eq(UserOrgEntity::getUserId, userEntity.getUserId());
            userOrgWrapper.eq(UserOrgEntity::getOrgId, orgEntity.getOrgId());
            List<UserOrgEntity> userOrgEntityList = userOrgService.list(userOrgWrapper);
            if (CollectionUtils.isEmpty(userOrgEntityList)) {
                loginLogService.addLoginLog(userEntity, false, "Invalid org role", ipAddress, userAgent);

                return HttpResultResponse.unauthorized("invalid org role");
            }
            UserOrgEntity userOrgEntity = userOrgEntityList.get(0);
            roleType = userOrgEntity.getRoleType();
        }

        // 创建token
        CustomClaim customClaim = new CustomClaim(userEntity.getUserId(),
                userEntity.getUsername(), userEntity.getUserType(),
                roleType,
                workspaceOpt.get().getWorkspaceId(), orgEntity.getOrgId());

        // create token
        String token = JwtUtil.createToken(customClaim.convertToMap());

        UserDTO userDTO = entityConvertToDTO(userEntity);
        userDTO.setMqttAddr(MqttPropertyConfiguration.getBasicMqttAddress());
        userDTO.setAccessToken(token);
        userDTO.setWorkspaceId(workspaceOpt.get().getWorkspaceId());
        userDTO.setRoleType(roleType);
        userDTO.setOrgId(orgEntity.getOrgId());

        if (StringUtils.hasText(orgEntity.getLogo())) {
            URL objectUrl = ossService.getObjectUrl(OssConfiguration.bucket, orgEntity.getLogo());
            if (objectUrl != null) {
                userDTO.setOrgLogo(objectUrl.toString());
            }
        }

        loginLogService.addLoginLog(userEntity, true, "Login successful", ipAddress, userAgent);

        // Flag whether the password is expired / first-time login, so the front-end can redirect to change-password
        boolean pwdExpired = isPasswordExpired(userEntity.getUserId());
        userDTO.setPasswordExpired(pwdExpired);

        return HttpResultResponse.success(userDTO);
    }

    @Override
    public HttpResultResponse userLogin(String username, String password, Integer flag) {
        // check user
        UserEntity userEntity = this.getUserByUsername(username);
        if (userEntity == null) {
            return new HttpResultResponse()
                    .setCode(HttpStatus.UNAUTHORIZED.value())
                    .setMessage("invalid username");
        }
        // 修改逻辑 跳过web判定
        if (flag != UserTypeEnum.WEB.getVal()) {
            if (flag.intValue() != userEntity.getUserType().intValue()) {
                return HttpResultResponse.error("The account type does not match.");
            }
        }
        // 密码加密验证
        // if (!password.equals(userEntity.getPassword())) {
        if (!SecurityUtils.matchesPassword(password, userEntity.getPassword())) {
            return new HttpResultResponse()
                    .setCode(HttpStatus.UNAUTHORIZED.value())
                    .setMessage("invalid password");
        }

        Optional<WorkspaceDTO> workspaceOpt = workspaceService.getWorkspaceByWorkspaceId(userEntity.getWorkspaceId());
        if (workspaceOpt.isEmpty()) {
            return new HttpResultResponse()
                    .setCode(HttpStatus.UNAUTHORIZED.value())
                    .setMessage("invalid workspace id");
        }

        // 创建token
        CustomClaim customClaim = new CustomClaim(userEntity.getUserId(),
                userEntity.getUsername(), userEntity.getUserType(),
                userEntity.getRoleType(),
                workspaceOpt.get().getWorkspaceId(), null);

        // create token
        String token = JwtUtil.createToken(customClaim.convertToMap());

        UserDTO userDTO = entityConvertToDTO(userEntity);
        userDTO.setMqttAddr(MqttPropertyConfiguration.getBasicMqttAddress());
        userDTO.setAccessToken(token);
        userDTO.setWorkspaceId(workspaceOpt.get().getWorkspaceId());
        return HttpResultResponse.success(userDTO);
    }

    @Override
    public Optional<UserDTO> refreshToken(String token) {
        if (!StringUtils.hasText(token)) {
            return Optional.empty();
        }
        CustomClaim customClaim;
        try {
            DecodedJWT jwt = JwtUtil.verifyToken(token);
            customClaim = new CustomClaim(jwt.getClaims());
        } catch (TokenExpiredException e) {
            customClaim = new CustomClaim(JWT.decode(token).getClaims());
        } catch (Exception e) {
            e.printStackTrace();
            return Optional.empty();
        }
        String refreshToken = JwtUtil.createToken(customClaim.convertToMap());

        UserDTO user = entityConvertToDTO(this.getUserByUsername(customClaim.getUsername()));
        if (Objects.isNull(user)) {
            return Optional.empty();
        }
        user.setWorkspaceId(customClaim.getWorkspaceId());
        user.setAccessToken(refreshToken);
        user.setRoleType(customClaim.getRoleType());
        user.setOrgId(customClaim.getOrgId());
        return Optional.of(user);
    }

    /**
     * Create a user object containing a new token.
     * @param token
     * @return
     */
    @Override
    public Optional<UserDTO> changeOrgRefreshToken(String token, String orgId) {
        if (!StringUtils.hasText(token)) {
            return Optional.empty();
        }
        if (!StringUtils.hasText(orgId)) {
            return Optional.empty();
        }

        CustomClaim customClaim;
        try {
            DecodedJWT jwt = JwtUtil.verifyToken(token);
            customClaim = new CustomClaim(jwt.getClaims());
        } catch (TokenExpiredException e) {
            customClaim = new CustomClaim(JWT.decode(token).getClaims());
        } catch (Exception e) {
            e.printStackTrace();
            return Optional.empty();
        }

        // 查询权限
        Integer roleType;
        // 组织 验证
        customClaim.setOrgId(orgId);
        if (customClaim.getRoleType() == RoleTypeEnum.SYS_ADMIN.getVal()) {
            roleType = customClaim.getRoleType();
        } else {
            // 查询团队权限
            LambdaQueryWrapper<UserOrgEntity> userOrgWrapper = new LambdaQueryWrapper<>();
            userOrgWrapper.eq(UserOrgEntity::getUserId, customClaim.getId());
            userOrgWrapper.eq(UserOrgEntity::getOrgId, orgId);
            List<UserOrgEntity> userOrgEntityList = userOrgService.list(userOrgWrapper);
            if (CollectionUtils.isEmpty(userOrgEntityList)) {
                throw new RuntimeException("invalid org role");
            }
            UserOrgEntity userOrgEntity = userOrgEntityList.get(0);
            roleType = userOrgEntity.getRoleType();
            customClaim.setRoleType(roleType);
        }

        String refreshToken = JwtUtil.createToken(customClaim.convertToMap());

        UserDTO user = entityConvertToDTO(this.getUserByUsername(customClaim.getUsername()));
        if (Objects.isNull(user)) {
            return Optional.empty();
        }
        user.setWorkspaceId(customClaim.getWorkspaceId());
        user.setAccessToken(refreshToken);
        user.setRoleType(roleType);
        user.setOrgId(orgId);
        return Optional.of(user);
    }
    @Override
    public PaginationData<UserListDTO> getUsersByWorkspaceId(long page, long pageSize, String workspaceId) {
        Page<UserEntity> userEntityPage = mapper.selectPage(
                new Page<>(page, pageSize),
                new LambdaQueryWrapper<UserEntity>().eq(UserEntity::getWorkspaceId, workspaceId));

        List<UserListDTO> usersList = userEntityPage.getRecords()
                .stream()
                .map(this::entity2UserListDTO)
                .collect(Collectors.toList());
        return new PaginationData<>(usersList, new Pagination(userEntityPage.getCurrent(), userEntityPage.getSize(), userEntityPage.getTotal()));
    }

    /**
     * Query information about all users.
     * @param workspaceId   uuid
     * @return
     */
    @Override
    public PaginationData<UserListDTO> getUsers(UserSearchParam param, String workspaceId, long page, long pageSize) {
        LambdaQueryWrapper<UserEntity> userQueryWrapper = new LambdaQueryWrapper<>();
        userQueryWrapper.eq(UserEntity::getWorkspaceId, workspaceId);

        // 查询指定 团队的人员
        if (StringUtils.hasText(param.getOrgId())) {
            // 需要增加限制
            LambdaQueryWrapper<UserOrgEntity> userOrgQueryWrapper = new LambdaQueryWrapper<>();
            userOrgQueryWrapper.eq(UserOrgEntity::getWorkspaceId, workspaceId);
            userOrgQueryWrapper.eq(UserOrgEntity::getOrgId, param.getOrgId());
            List<UserOrgEntity> userOrgEntities = userOrgService.list(userOrgQueryWrapper);
            if (CollectionUtils.isEmpty(userOrgEntities)) {
                return new PaginationData<>();
            }
            List<String> userOrgUserIdList = userOrgEntities.stream().map(UserOrgEntity::getUserId).distinct().collect(Collectors.toList());
            if (CollectionUtils.isEmpty(userOrgUserIdList)) {
                return new PaginationData<>();
            }
            userQueryWrapper.in(UserEntity::getUserId, userOrgUserIdList);
        } else {
            // 假如不是 系统管理员 只能查询自己团队的用户
            if (aboveSysAdminRole()) {

            } else {
                String orgId = getOrgId();
                LambdaQueryWrapper<UserOrgEntity> userOrgQueryWrapper = new LambdaQueryWrapper<>();
                userOrgQueryWrapper.eq(UserOrgEntity::getWorkspaceId, workspaceId);
                userOrgQueryWrapper.eq(UserOrgEntity::getOrgId, orgId);
                List<UserOrgEntity> userOrgEntities = userOrgService.list(userOrgQueryWrapper);
                if (CollectionUtils.isEmpty(userOrgEntities)) {
                    return new PaginationData<>();
                }
                List<String> userOrgUserIdList = userOrgEntities.stream().map(UserOrgEntity::getUserId).distinct().collect(Collectors.toList());
                if (CollectionUtils.isEmpty(userOrgUserIdList)) {
                    return new PaginationData<>();
                }
                userQueryWrapper.in(UserEntity::getUserId, userOrgUserIdList);
            }
        }

        Page<UserEntity> userEntityPage = this.page(new Page<>(page, pageSize), userQueryWrapper);
        List<UserListDTO> usersList = userEntityPage.getRecords()
                .stream()
                .map(this::entity2UserListDTO)
                .collect(Collectors.toList());
        return new PaginationData<>(usersList, new Pagination(userEntityPage.getCurrent(), userEntityPage.getSize(), userEntityPage.getTotal()));
    }

    @Override
    public Boolean updateUser(String workspaceId, String userId, UserListDTO user) {
        UserEntity userEntity = mapper.selectOne(
                new LambdaQueryWrapper<UserEntity>()
                        .eq(UserEntity::getUserId, userId)
                        .eq(UserEntity::getWorkspaceId, workspaceId));
        if (userEntity == null) {
            return false;
        }
        userEntity.setMqttUsername(user.getMqttUsername());
        userEntity.setMqttPassword(user.getMqttPassword());
        userEntity.setUpdateTime(System.currentTimeMillis());
        int id = mapper.update(userEntity, new LambdaUpdateWrapper<UserEntity>()
                .eq(UserEntity::getUserId, userId)
                .eq(UserEntity::getWorkspaceId, workspaceId));

        return id > 0;
    }

    @Override
    public Boolean deleteUser(String workspaceId, String userId) {

        // 管理员以上才能删除
        aboveAdminRoleAndThrowError();
//        if (isNotAdmin()) {
//            throw new RuntimeException("The current user is not an admin and has no permissions");
//        }
        LambdaQueryWrapper<UserEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserEntity::getWorkspaceId, workspaceId);
        queryWrapper.eq(UserEntity::getUserId, userId);

        // 需要先查询
        UserEntity userEntity = this.mapper.selectOne(queryWrapper);
        // 不能删除自身
        if (userEntity.getUserId().equals(getUserId())) {
            throw new RuntimeException("can't delete self");
        }
        // 不能删除高级用户
        if (userEntity.getRoleType() == RoleTypeEnum.SYS_ADMIN.getVal()) {
            aboveSysAdminRoleAndThrowError();
        }
        int delete;
        if (aboveSysAdminRole()) {
            // 超级管理员 直接删除用户
            this.removeById(userEntity.getId());

            // 删除相关用户组织表
            LambdaQueryWrapper<UserOrgEntity> delUserOrgQueryWrapper = new LambdaQueryWrapper<>();
            delUserOrgQueryWrapper.eq(UserOrgEntity::getUserId, userEntity.getUserId());
            delUserOrgQueryWrapper.eq(UserOrgEntity::getWorkspaceId, userEntity.getWorkspaceId());
            boolean userOrgDelRes = userOrgService.remove(delUserOrgQueryWrapper);

        } else {
            // 普通管理员 删除用户，只删用户当前组织联系，当唯一关联才删用户

            // 查询 组织关系
            LambdaQueryWrapper<UserOrgEntity> userOrgQueryWrapper = new LambdaQueryWrapper<>();
            userOrgQueryWrapper.eq(UserOrgEntity::getUserId, userEntity.getUserId());
            userOrgQueryWrapper.eq(UserOrgEntity::getWorkspaceId, userEntity.getWorkspaceId());
            List<UserOrgEntity> userOrgEntities = userOrgService.list(userOrgQueryWrapper);

            // 没有组织关联 直接删除
            if (CollectionUtils.isEmpty(userOrgEntities)) {
                delete = mapper.delete(queryWrapper);
            }

            // 判断是否 有其他组织关系
            List<UserOrgEntity> curUserOrgList = userOrgEntities.stream().filter(x -> getOrgId().equals(x.getOrgId())).collect(Collectors.toList());
            List<UserOrgEntity> otherUserOrgList = userOrgEntities.stream().filter(x -> !getOrgId().equals(x.getOrgId())).collect(Collectors.toList());

            if (!CollectionUtils.isEmpty(curUserOrgList)) {
                UserOrgEntity userOrgEntity = curUserOrgList.get(0);
                //        if (userEntity.getUserType() == UserTypeEnum.WEB.getVal()) {
                //            throw new RuntimeException("Failed to delete admin");
                //        }
                // 然后 假如是删除管理员 不能删除 项目管理员
                if (userEntity.getRoleType() == RoleTypeEnum.ORG_ADMIN.getVal()) {
                    throw new RuntimeException("can't delete workspace project admin");
                }
                if (userEntity.getRoleType() == RoleTypeEnum.ADMIN.getVal()) {
                    LambdaQueryWrapper<WorkspaceEntity> workspaceWrapper = new LambdaQueryWrapper<>();
                    workspaceWrapper.eq(WorkspaceEntity::getAdminUserId, userEntity.getUserId());
                    List<WorkspaceEntity> workspaceEntities = workspaceService.list(workspaceWrapper);
                    if (!CollectionUtils.isEmpty(workspaceEntities)) {
                        throw new RuntimeException("can't delete workspace admin");
                    }
                }
                // 删除相关用户组织表
                LambdaQueryWrapper<UserOrgEntity> delUserOrgQueryWrapper = new LambdaQueryWrapper<>();
                delUserOrgQueryWrapper.eq(UserOrgEntity::getUserId, userEntity.getUserId());
                delUserOrgQueryWrapper.eq(UserOrgEntity::getWorkspaceId, userEntity.getWorkspaceId());
                delUserOrgQueryWrapper.eq(UserOrgEntity::getOrgId, getOrgId());
                boolean userOrgDelRes = userOrgService.remove(delUserOrgQueryWrapper);
            }
            // 没有其他组织 则删除用户
            if (CollectionUtils.isEmpty(otherUserOrgList)) {
                delete = mapper.delete(queryWrapper);
            }
        }
        return true;
    }


    @Override
    public Boolean addUser(String workspaceId, UserEntity user) {

        // 管理员才能创建用户
        aboveAdminRoleAndThrowError();

        // 用户名不能重复
        String username = user.getUsername();
        LambdaQueryWrapper<UserEntity> userQueryWrapper = new LambdaQueryWrapper<>();
        userQueryWrapper.eq(UserEntity::getUsername, username);
        // userQueryWrapper.eq(UserEntity::getWorkspaceId, workspaceId);
        List<UserEntity> nameUserList = this.mapper.selectList(userQueryWrapper);
        if (!CollectionUtils.isEmpty(nameUserList)) {
            throw new RuntimeException("the username is already existed");
        }

        UserEntity userEntity = new UserEntity();

        // 普通用户不能创建管理员
//        if (user.getUserType() == UserTypeEnum.WEB.getVal()) {
//            if (isNotAdmin()) {
//                throw new RuntimeException("The current user is not an admin and has no permissions");
//            }
//        }

        // 不能创建高级用户
        if (user.getRoleType() == RoleTypeEnum.SYS_ADMIN.getVal()) {
            aboveSysAdminRoleAndThrowError();
        }

        userEntity.setUserType(user.getUserType() != null ? user.getUserType() : UserTypeEnum.PILOT.getVal());
        userEntity.setRoleType(user.getRoleType() != null ? user.getRoleType() : RoleTypeEnum.MEMBER.getVal());
        userEntity.setUserId(UUID.randomUUID().toString());
        userEntity.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
        userEntity.setUsername(user.getUsername());
        userEntity.setWorkspaceId(workspaceId);

        int insert = this.mapper.insert(userEntity);

        // 增加用户权限
        UserOrgEntity userOrgEntity = new UserOrgEntity();
        userOrgEntity.setWorkspaceId(workspaceId);
        userOrgEntity.setOrgId(getOrgId());
        userOrgEntity.setUserId(userEntity.getUserId());
        userOrgEntity.setRoleType(user.getRoleType() != null ? user.getRoleType() : RoleTypeEnum.MEMBER.getVal());
        // private Integer status;
        userOrgEntity.setCreatorId(getUserId());
        userOrgEntity.setCreatorName(getUsername());
        userOrgEntity.setCreateTime(System.currentTimeMillis());
        userOrgEntity.setUpdaterId(getUserId());
        userOrgEntity.setUpdaterName(getUsername());
        userOrgEntity.setUpdateTime(System.currentTimeMillis());
        boolean userOrgSaveRes = userOrgService.save(userOrgEntity);

        return insert > 0;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public UserEntity addWorkspaceAdminUser(UserEntity user) {

        // 系统管理员才能创建
        aboveSysAdminRoleAndThrowError();

        // 用户名不能重复
        String username = user.getUsername();
        LambdaQueryWrapper<UserEntity> userQueryWrapper = new LambdaQueryWrapper<>();
        userQueryWrapper.eq(UserEntity::getUsername, username);
        // userQueryWrapper.eq(UserEntity::getWorkspaceId, user.getWorkspaceId());
        List<UserEntity> nameUserList = this.list(userQueryWrapper);
        if (!CollectionUtils.isEmpty(nameUserList)) {
            throw new RuntimeException("the username is already existed");
        }

        UserEntity userEntity = new UserEntity();

        userEntity.setUserType(UserTypeEnum.WEB.getVal());
        userEntity.setRoleType(RoleTypeEnum.ADMIN.getVal());
        userEntity.setUserId(UUID.randomUUID().toString());
        userEntity.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
        userEntity.setUsername(user.getUsername());
        userEntity.setWorkspaceId(user.getWorkspaceId());

        boolean save = this.save(userEntity);

        return userEntity;

    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public UserEntity addOrgAdminUser(UserEntity user) {

        // 系统管理员才能创建
        aboveSysAdminRoleAndThrowError();

        // 判断用户是否存在 用户存在直接返回
        // 用户名不能重复
        String username = user.getUsername();
        LambdaQueryWrapper<UserEntity> userQueryWrapper = new LambdaQueryWrapper<>();
        userQueryWrapper.eq(UserEntity::getUsername, username);
        // userQueryWrapper.eq(UserEntity::getWorkspaceId, user.getWorkspaceId());
        List<UserEntity> nameUserList = this.list(userQueryWrapper);
        if (!CollectionUtils.isEmpty(nameUserList)) {
            // throw new RuntimeException("the username is already existed");
            return nameUserList.get(0);
        }

        UserEntity userEntity = new UserEntity();

        userEntity.setUserType(UserTypeEnum.WEB.getVal());
        userEntity.setRoleType(RoleTypeEnum.ADMIN.getVal());
        userEntity.setUserId(UUID.randomUUID().toString());
        userEntity.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
        userEntity.setUsername(user.getUsername());
        userEntity.setWorkspaceId(user.getWorkspaceId());

        boolean save = this.save(userEntity);

        return userEntity;

    }

    /**
     * Convert database entity objects into user data transfer object.
     * @param entity
     * @return
     */
    private UserListDTO entity2UserListDTO(UserEntity entity) {
        UserListDTO.UserListDTOBuilder builder = UserListDTO.builder();
        if (entity != null) {
            builder.userId(entity.getUserId())
                    .id(entity.getId())
                    .username(entity.getUsername())
                    .mqttUsername(entity.getMqttUsername())
                    .mqttPassword(entity.getMqttPassword())
                    .userType(UserTypeEnum.find(entity.getUserType()).getDesc())
                    .roleTypeName(RoleTypeEnum.find(entity.getRoleType()).getDesc())
                    .roleType(entity.getRoleType())
                    .createTime(LocalDateTime.ofInstant(
                            Instant.ofEpochMilli(entity.getCreateTime()), ZoneId.systemDefault()));
            Optional<WorkspaceDTO> workspaceOpt = workspaceService.getWorkspaceByWorkspaceId(entity.getWorkspaceId());
            workspaceOpt.ifPresent(workspace -> builder.workspaceName(workspace.getWorkspaceName()));
        }

        return builder.build();
    }

    /**
     * Query a user by username.
     * @param username
     * @return
     */
    private UserEntity getUserByUsername(String username) {
        return mapper.selectOne(new QueryWrapper<UserEntity>()
                .eq("username", username));
    }

    /**
     * Query a user by username.
     * @param userId
     * @return
     */
    private UserEntity getUserByUserId(String userId) {
        return mapper.selectOne(new QueryWrapper<UserEntity>()
                .eq("user_id", userId));
    }

    /**
     * Convert database entity objects into user data transfer object.
     * @param entity
     * @return
     */
    private UserDTO entityConvertToDTO(UserEntity entity) {
        if (entity == null) {
            return null;
        }
        return UserDTO.builder()
                .userId(entity.getUserId())
                .username(entity.getUsername())
                .userType(entity.getUserType())
                .roleType(entity.getRoleType())
                .mqttUsername(entity.getMqttUsername())
                .mqttPassword(entity.getMqttPassword())
                .mqttAddr(MqttPropertyConfiguration.getBasicMqttAddress())
                .build();
    }

    /**
     * Query user's details based on userId
     * @param userId
     * @param workspaceId
     * @return
     */
    @Override
    public HttpResultResponse getUserByUserId(String userId, String workspaceId) {

        UserEntity userEntity = this.getUserByUserId(userId);
        if (userEntity == null) {
            return new HttpResultResponse()
                    .setCode(HttpStatus.UNAUTHORIZED.value())
                    .setMessage("invalid userId");
        }

        UserDTO user = this.entityConvertToDTO(userEntity);
        user.setWorkspaceId(workspaceId);

        return HttpResultResponse.success(user);
    }
}
