package org.lingniu.idp.service.core.login;

import org.lingniu.idp.common.redis.RedisCache;
import org.lingniu.idp.constant.CacheConstants;
import org.lingniu.idp.constant.Constants;
import org.lingniu.idp.constant.UserConstants;
import org.lingniu.idp.exception.user.BlackListException;
import org.lingniu.idp.exception.user.UserNotExistsException;
import org.lingniu.idp.exception.user.UserPasswordNotMatchException;
import org.lingniu.idp.exception.user.UserPasswordRetryLimitExceedException;
import org.lingniu.idp.manager.AsyncManager;
import org.lingniu.idp.manager.factory.AsyncFactory;
import org.lingniu.idp.model.dto.LoginUser;
import org.lingniu.idp.model.entity.SysUser;
import org.lingniu.idp.model.vo.DataPermission;
import org.lingniu.idp.service.ISysConfigService;
import org.lingniu.idp.service.ISysUserService;
import org.lingniu.idp.service.core.CaptchaService;
import org.lingniu.idp.service.core.SysPermissionService;
import org.lingniu.idp.utils.MessageUtils;
import org.lingniu.idp.utils.SecurityUtils;
import org.lingniu.idp.utils.StringUtils;
import org.lingniu.idp.utils.ip.IpUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;


/**
 * 登录校验方法
 * 
 * @author portal
 */
@Component
public class LoginService
{

    @Autowired
    private RedisCache redisCache;

    @Value(value = "${user.password.maxRetryCount}")
    private int maxRetryCount;

    @Value(value = "${user.password.lockTime}")
    private int lockTime;
    
    @Autowired
    private ISysUserService userService;

    @Autowired
    private ISysConfigService configService;
    @Autowired
    private CaptchaService captchaService;

    @Autowired
    private SysPermissionService permissionService;
    /**
     * 登录验证
     * 
     * @param username 用户名
     * @param password 密码
     * @param code 验证码
     * @param uuid 唯一标识
     * @return 结果
     */
    public LoginUser login(String username, String password, String code, String uuid)
    {
        // 验证码校验
        captchaService.validateCaptcha(username, code, uuid);
        // 登录前置校验
        loginPreCheck(username, password);
        // 2. 加载用户
        SysUser sysUser = userService.selectUserByUserName(username);
        Integer retryCount = redisCache.getCacheObject(getCacheKey(username));

        if (retryCount == null)
        {
            retryCount = 0;
        }

        if (retryCount >= Integer.valueOf(maxRetryCount).intValue())
        {
            throw new UserPasswordRetryLimitExceedException(maxRetryCount, lockTime);
        }

        if (!matches(sysUser.getPassword(), password))
        {
            retryCount = retryCount + 1;
            redisCache.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES);
            throw new UserPasswordNotMatchException();
        }
        else
        {
            clearLoginRecordCache(username);
        }
        return new LoginUser(sysUser, permissionService.getMenuPermission(sysUser));
    }

    /**
     * 登录前置校验
     * @param username 用户名
     * @param password 用户密码
     */
    public void loginPreCheck(String username, String password)
    {
        // 用户名或密码为空 错误
        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password))
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
            throw new UserNotExistsException();
        }
        // 密码如果不在指定范围内 错误
        if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
                || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
            throw new UserPasswordNotMatchException();
        }
        // 用户名不在指定范围内 错误
        if (username.length() < UserConstants.USERNAME_MIN_LENGTH
                || username.length() > UserConstants.USERNAME_MAX_LENGTH)
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
            throw new UserPasswordNotMatchException();
        }
        // IP黑名单校验
        String blackStr = configService.selectConfigByKey("sys.login.blackIPList");
        if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr()))
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked")));
            throw new BlackListException();
        }
    }
    public Map<String,Object> getPermissionInfo(SysUser sysUser){
        // 角色集合
        Set<String> roles = permissionService.getRolePermission(sysUser);
        // 权限集合
        Set<String> permissions = permissionService.getMenuPermission(sysUser);
        // 数据权限
        DataPermission dataPermission = permissionService.getDataPermission(sysUser);

        Map<String,Object> map = new HashMap<>();
        map.put("roles",roles);
        map.put("permissions",permissions);
        map.put("dataPermission",dataPermission);

        return map;
    }
    public SysUser getUserDetail(String userName){
        SysUser sysUser = userService.selectUserByUserName(userName);

        return sysUser;
    }
    private String getCacheKey(String username)
    {
        return CacheConstants.PWD_ERR_CNT_KEY + username;
    }


    public boolean matches(String password, String rawPassword)
    {
        return SecurityUtils.matchesPassword(rawPassword, password);
    }

    public void clearLoginRecordCache(String loginName)
    {
        if (redisCache.hasKey(getCacheKey(loginName)))
        {
            redisCache.deleteObject(getCacheKey(loginName));
        }
    }
}
