/*
 * Copyright (c) 2023 codvision.com All Rights Reserved.
 */

package com.codvision.auth.core;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.codvision.auth.core.token.ITokenManager;
import com.codvision.auth.entity.vo.SysRoleVO;
import com.codvision.auth.entity.vo.SysUserVO;
import com.codvision.auth.service.ISysRoleService;
import com.codvision.auth.service.ISysUserService;
import com.codvision.auth.support.UserContext;
import com.codvision.authcore.bean.SysMenuBean;
import com.codvision.authcore.bean.SysUserBean;
import com.codvision.authcore.config.AuthProperties;
import com.codvision.authcore.service.IAuthService;
import com.codvision.authcore.util.RequestUtil;
import com.codvision.cachecore.utils.RedisUtil;
import com.codvision.commoncore.exception.AuthException;
import com.codvision.commoncore.exception.BusinessException;
import com.codvision.commoncore.utils.BeanUtil;
import com.codvision.commoncore.utils.DecPwdUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.codvision.auth.support.AuthConst.LOCK;
import static com.codvision.auth.support.AuthConst.USER_LOCK_LIMIT;
import static com.codvision.auth.support.CacheContants.USER_LOGIN_COUNT;
import static com.codvision.auth.support.CacheContants.USER_LOGIN_LOCK;
import static com.codvision.authcore.constants.AuthCoreConstant.IframeDownFlag;

/**
 * 认证中心扩展公共认证模块接口
 * <p>
 * 通过策略模式切换单体程序和微服务的用户认证
 *
 * @author lingee
 * @date 2023/11/22
 */
public class AuthCenterService implements IAuthService {

    @Resource
    private ISysUserService sysUserService;

    @Resource
    private BCryptPasswordEncoder bCryptEncode;

    @Resource
    private ITokenManager tokenManager;

    @Resource
    private AuthProperties authProperties;

    @Resource
    private ISysRoleService sysRoleService;

    @Override
    public SysUserBean getCurrentUserInfo(String token) {
        return tokenManager.getTokenUser(token);
    }

    @Override
    public String getAuthToken(HttpServletRequest request) {
        String tokenName = authProperties.getTokenName();
        String cookieName = authProperties.getCookieName();
        String token = request.getParameter(tokenName);

        if (StrUtil.isNotBlank(token)) {
            return token;
        }

        token = RequestUtil.getFromCookie(request, cookieName);
        if (StrUtil.isNotBlank(token)) {
            return token;
        }

        token = request.getHeader(tokenName);
        if (StrUtil.isNotBlank(token)) {
            return token;
        }

        final String iframeDown = request.getParameter(IframeDownFlag);
        if (StrUtil.isNotBlank(iframeDown)) {
            token = request.getParameter(tokenName);
        }


        if (StrUtil.isBlank(token)) {
            token = request.getSession().getId();
        }
        return token;
    }

    @Override
    public List<SysMenuBean> getMenuList(String token) {
        SysUserBean sysUser = getCurrentUserInfo(token);
        SysRoleVO sysRoleVO = sysRoleService.getDetail(sysUser.getActiveRoleId());
        if (ObjectUtil.isNotEmpty(sysRoleVO)) {
            return BeanUtil.copyToList(sysRoleVO.getRoleMenus(), SysMenuBean.class);
        }
        return null;
    }

    @Override
    public SysUserBean loginByUsernameAndPassword(String username, String password) {
        if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
            throw new BusinessException(30002);
        }

        RedisUtil.increment(USER_LOGIN_COUNT + username, 1);
        String lock = RedisUtil.get(USER_LOGIN_LOCK + username);
        if (LOCK.equals(lock)) {
            long expire = RedisUtil.getExpire(USER_LOGIN_LOCK + username);
            String msg = (expire % 3600) / 60 == 0 ? expire % 60 + "秒" : (expire % 3600) / 60 + "分钟" + expire % 60 + "秒";
            throw new AuthException("您的帐号[" + username + "]已被禁止登录！将于" + msg + "后自动解除锁定状态");
        }

        SysUserVO sysUserVO = sysUserService.getDetailByLoginName(username);
        if (ObjectUtil.isEmpty(sysUserVO)) {
            throw new AuthException(10001);
        }
        // 数据库密码
        String dbPassword = sysUserVO.getPassword();
        if (StringUtils.isBlank(dbPassword)) {
            throw new AuthException("当前账号不支持使用密码登陆的方式，请更换其他登陆方式");
        }
        if (!bCryptEncode.matches(DecPwdUtil.decryptAes(password), dbPassword)) {
            Integer count = RedisUtil.get(USER_LOGIN_COUNT + username);
            int retryCount = USER_LOCK_LIMIT - count;
            boolean flag = retryCount <= 0;
            if (flag) {
                RedisUtil.expire(USER_LOGIN_COUNT + username, 10, TimeUnit.MINUTES);
                RedisUtil.set(USER_LOGIN_LOCK + username, LOCK, 10, TimeUnit.MINUTES);
            }
            String msg = flag ? "您的帐号[" + username + "]十分钟内已被禁止登录" : "您还剩" + retryCount + "次重试的机会";
            throw new AuthException("用户名或密码错误！" + msg);
        }

        // 清空计数
        RedisUtil.del(USER_LOGIN_COUNT + username, USER_LOGIN_LOCK + username);

        return UserContext.set(sysUserVO);
    }
}
