package com.lion.config;

import com.lion.entity.LoginUserInfo;
import com.lion.util.PropertiesUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

// zhao@20230427: 用户登录失败记录缓存
public final class UserLoginRecordCache
{
	private static final Logger                     logger = LoggerFactory.getLogger(UserLoginRecordCache.class);
	public static final  Map<String, LoginUserInfo> cache  = new HashMap<>();

	private static Integer FAIL_COUNT_LIMIT;
	private static Integer FAIL_LIMIT_TIME;
	private static boolean inited = false;

	private static void Init()
	{
		if(inited)
			return;
		FAIL_COUNT_LIMIT = ParseInt(PropertiesUtil.getProperty("USER_LOGIN.FAIL_COUNT_LIMIT"));
		FAIL_LIMIT_TIME = ParseInt(PropertiesUtil.getProperty("USER_LOGIN.FAIL_LIMIT_TIME"));
		inited = true;
	}

	private static int ParseInt(String val)
	{
		if(StringUtils.isEmpty(val))
			return 0;
		try
		{
			return Integer.parseInt(val);
		}
		catch(Exception e)
		{
			e.printStackTrace();
			return 0;
		}
	}

	public static String BeginUserLogin(String userId)
	{
		Init();
		synchronized(cache) {
			long time;
			LoginUserInfo user;

			if(FAIL_COUNT_LIMIT <= 0)
				return null;

			if(!cache.containsKey(userId)) // 未登录过
			{
				user = new LoginUserInfo();
				user.setUserAccount(userId);
				user.LoginFailCount(0);
				user.LoginFailPunishStartTime(0);
				cache.put(userId, user);
				LOG("用户 {} 从未登录", userId);
				return null;
			}
			else
				user = cache.get(userId);

			if(FAIL_LIMIT_TIME > 0)
			{
				time = System.currentTimeMillis();
				long loginFailPunishStartTime = user.LoginFailPunishStartTime();
				if(loginFailPunishStartTime > 0) // 锁定中
				{
					if(time - loginFailPunishStartTime > FAIL_LIMIT_TIME * 1000) // 过了锁定时间
					{
						LOG("用户 {} 登录锁定中且过了锁定时间: {} - {} > {}秒", userId, date_format(time), date_format(loginFailPunishStartTime), FAIL_LIMIT_TIME);
						user.LoginFailPunishStartTime(0);
						user.LoginFailCount(0);
						return null;
					}
					else // 还在锁定时间
					{
						LOG("用户 {} 登录锁定中: {} - {} <= {}秒", userId, date_format(time), date_format(loginFailPunishStartTime), FAIL_LIMIT_TIME);
						return "该用户登录失败已超过限制次数, 锁定登录中";
					}
				}
				else // 未锁定
				{
					if(user.LoginFailCount() >= FAIL_COUNT_LIMIT) // 超过限制
					{
						user.LoginFailPunishStartTime(time);
						LOG("用户 {} 锁定登录: {}", userId, date_format(time));
						return "该用户登录失败已超过限制次数, 将锁定登录";
					}
					else // 还有次数
					{
						LOG("用户 {} 允许登录: {} < {}", userId, user.LoginFailCount(), FAIL_COUNT_LIMIT);
						return null;
					}
				}
			}
			else
				return null;
		}
	}

	public static boolean EndUserLogin(String userId, boolean success)
	{
		synchronized(cache) {
			long time;
			LoginUserInfo user;

			if(FAIL_COUNT_LIMIT <= 0)
				return false;
			user = cache.get(userId);

			if(success) // 登录成功清空失败记录
			{
				LOG("用户 {} 登录成功", userId);
				user.LoginFailPunishStartTime(0);
				user.LoginFailCount(0);
				return false;
			}

			time = System.currentTimeMillis();

			user.LoginFailCount(user.LoginFailCount() + 1);
			if(FAIL_LIMIT_TIME > 0)
			{
				if(user.LoginFailCount() >= FAIL_COUNT_LIMIT) // 超过失败次数
				{
					LOG("用户 {} 超出登录失败次数, 开始锁定: {} >= {}", userId, user.LoginFailCount(), FAIL_COUNT_LIMIT);
					user.LoginFailPunishStartTime(time); // 锁定
					return true;
				}
				else
				{
					LOG("用户 {} 登录失败剩余次数 {}", userId, FAIL_COUNT_LIMIT - user.LoginFailCount());
					user.LoginFailPunishStartTime(0); // 不锁定
					return false;
				}
			}
			else
			{
				user.LoginFailPunishStartTime(0);
				if(user.LoginFailCount() >= FAIL_COUNT_LIMIT)
					user.LoginFailCount(0);
				return false;
			}
		}
	}

	private static String date_format(long ts)
	{
		return DateFormatUtils.format(new Date(ts), "yyyy-MM-dd HH:mm:ss-SSS");
	}

	private static void LOG(Object obj, Object...args)
	{
		if(null == obj)
			logger.info("NULL");
		else
		{
			if(obj instanceof String)
			{
				logger.info((String)obj, args);
			}
			else
			{
				logger.info(obj.toString());
			}
		}
	}
}
