package com.ningdatech.carapi.user.security.auth.password;

import java.util.Objects;

import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ningdatech.basic.exception.BizException;
import com.ningdatech.cache.model.cache.CacheKey;
import com.ningdatech.cache.repository.CachePlusOps;
import com.ningdatech.carapi.sys.contants.UserLoginConstant;
import com.ningdatech.carapi.user.entity.NdUserAuth;
import com.ningdatech.carapi.user.service.INdUserAuthService;
import com.ningdatech.carapi.user.service.INdUserInfoService;

/**
 * @Author LiuXinXin
 * @Date 2020/8/3 8:55 下午
 * @Version 1.0
 **/
public class UsernamePasswordAuthProvider implements AuthenticationProvider {

    private UserDetailsService userDetailsService;
    private PasswordEncoder passwordEncoder;
    private INdUserInfoService ndUserInfoService;

    private INdUserAuthService ndUserAuthService;

    private CachePlusOps cachePlusOps;

    private Boolean smsCheck;

    public UsernamePasswordAuthProvider() {
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        if (!(authentication instanceof UsernamePasswordAuthToken)) {
            throw new RuntimeException("CustomAuthProvider 只支持 CustomAuthToken");
        }
        UsernamePasswordAuthToken authenticationToken = (UsernamePasswordAuthToken)authentication;
        // 获取用户名信息
        String userName = (String) authenticationToken.getPrincipal();

        //验证短信验证码
        if(smsCheck){
            smsCheck(authenticationToken, userName);
        }

        UserDetails user = userDetailsService.loadUserByUsername((String)authenticationToken.getPrincipal());
        if (user == null) {
            throw new InternalAuthenticationServiceException("获取不到用户信息!");
        }
        additionalAuthenticationChecks(user, authenticationToken);
        // 将用户定义的user放入token中，这样可以在session中查询到所有自定义的用户信息
        return new UsernamePasswordAuthToken(user, user.getPassword(), user.getAuthorities(),null);
    }

    private void smsCheck(UsernamePasswordAuthToken authenticationToken, String userName) {
        String code = authenticationToken.getVerificationCode();

        if (Objects.isNull(code)){
            throw new BizException("验证码未传!");
        }
        code = code.trim();

        // 将用户输入的验证码与redis中对应手机号的验证码做比较,判断验证码是否过期
        CacheKey key = new CacheKey(UserLoginConstant.RedisKey.USER_LOGIN + userName);
        String verificationCode = cachePlusOps.get(key);

        if (Objects.isNull(verificationCode)){
            throw new BizException("验证码失效,请重新获取!");
        }

        if (!code.equals(verificationCode)){
            throw new BizException("验证码输入错误，请重新输入!");
        }
    }

    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthToken authentication)
        throws AuthenticationException {
        String presentedUsername = authentication.getPrincipal().toString();
        NdUserAuth userAuth = ndUserAuthService.getOne(Wrappers.lambdaQuery(NdUserAuth.class)
                .eq(NdUserAuth::getIdentifier, presentedUsername));
        if (!userAuth.getIsAvailable()){
            throw new BizException("账号已被禁用，禁止登录!");
        }

        if (authentication.getCredentials() == null) {
            throw new BadCredentialsException("login fail! password is null");
        }
        String presentedPassword = authentication.getCredentials().toString();
        if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
            throw new BadCredentialsException("login fail! password is error");
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return UsernamePasswordAuthToken.class.isAssignableFrom(authentication);
    }

    public void setUserDetailsService(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
        this.passwordEncoder = passwordEncoder;
    }

    public void setNdUserInfoService(INdUserInfoService ndUserInfoService) {
        this.ndUserInfoService = ndUserInfoService;
    }

    public void setCachePlusOps(CachePlusOps cachePlusOps) {
        this.cachePlusOps = cachePlusOps;
    }

    public INdUserInfoService getNdUserInfoService() {
        return ndUserInfoService;
    }

    public CachePlusOps getCachePlusOps() {
        return cachePlusOps;
    }

    public INdUserAuthService getNdUserAuthService() {
        return ndUserAuthService;
    }

    public void setNdUserAuthService(INdUserAuthService ndUserAuthService) {
        this.ndUserAuthService = ndUserAuthService;
    }

    public Boolean getSmsCheck() {
        return smsCheck;
    }

    public void setSmsCheck(Boolean smsCheck) {
        this.smsCheck = smsCheck;
    }
}
