package com.xunhong.erp.turbo.user.app.executor.query;

import cn.hutool.crypto.digest.BCrypt;
import com.xunhong.erp.turbo.api.user.dto.enums.SocialTypeEnum;
import com.xunhong.erp.turbo.api.user.dto.qry.PasswordLoginQry;
import com.xunhong.erp.turbo.api.user.dto.vo.UserAuthVO;
import com.xunhong.erp.turbo.api.user.exception.UserErrorCode;
import com.xunhong.erp.turbo.api.user.exception.UserException;
import com.xunhong.erp.turbo.user.app.assembler.UserAuthAssembler;
import com.xunhong.erp.turbo.user.domain.entity.UserAuth;
import com.xunhong.erp.turbo.user.domain.gateway.UserAuthGateway;
import com.xunhong.erp.turbo.user.domain.gateway.UserGateway;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author shenyifei
 */
@Component
@RequiredArgsConstructor
public class PasswordLoginQryExe {
	private final UserAuthGateway userAuthGateway;
	private final UserAuthAssembler userAuthAssembler;
	private final RedissonClient redissonClient;

	private final UserGateway userGateway;

	public UserAuthVO execute(PasswordLoginQry passwordLoginQry) {
		String account = passwordLoginQry.getUsername();

		// 判断账号是否存在
		UserAuth userAuth = userAuthGateway.getByAccount(account, SocialTypeEnum.ACCOUNT);

		// 存在则提示账号已经存在
		if (Objects.isNull(userAuth)) {
			throw new UserException(UserErrorCode.B_USER_USER_AUTH_NULL);
		}

		// 检查账户是否被锁定
		RBucket<Boolean> isLockedBucket = redissonClient.getBucket("user:lock:" + account);
		if (isLockedBucket.isExists() && isLockedBucket.get()) {
			throw new UserException(UserErrorCode.B_USER_USER_AUTH_LOCKED);
		}

		// 密码校验
		if (!BCrypt.checkpw(passwordLoginQry.getPassword(), userAuth.getPassword())) {
			// 增加密码错误次数
			RBucket<Integer> errorCountBucket = redissonClient.getBucket("user:errorCount:" + account);
			int errorCount = errorCountBucket.isExists() ? errorCountBucket.get() : 0;
			errorCount++;

			if (errorCount >= 5) {
				// 锁定账户并设置30分钟过期时间
				RBucket<Boolean> isLockedBucket1 = redissonClient.getBucket("user:lock:" + account);
				isLockedBucket1.set(true, 30, TimeUnit.MINUTES);
				// 删除错误计数
				errorCountBucket.delete();
				throw new UserException(UserErrorCode.B_USER_USER_AUTH_LOCKED);
			} else {
				// 更新错误计数
				errorCountBucket.set(errorCount);
				String message = String.format("密码错误次数：%s 次，5 次后将锁定账户 30 分钟", errorCount);
				throw new UserException(message, UserErrorCode.B_USER_USER_AUTH_PASSWORD_ERROR);
			}
		} else {
			// 密码正确，重置错误计数
			RBucket<Integer> errorCountBucket = redissonClient.getBucket("user:errorCount:" + account);
			if (errorCountBucket.isExists()) {
				errorCountBucket.delete();
			}
		}

		// 更新登录时间
		LocalDateTime lastLoginAt = LocalDateTime.now();
		userGateway.updateLastLoginAt(userAuth.getUserId(), lastLoginAt);

		userAuth.getUser().setLastLoginAt(lastLoginAt);
		// 返回数据
		return userAuthAssembler.toUserAuthVO(userAuth);
	}
}
