package com.bcxin.survey.service.security;

import com.bcxin.survey.utils.Const;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.*;
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.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

@Component
public class UserAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Autowired
    private PasswordEncoder passwordEncoder;
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // [1] token 中的用户名和密码都是用户输入的，不是数据库里的
        UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
        String userName = token.getName().replaceAll(Const.USER_ACCOUNT_KEY,""); //用户名,替换免登录加密KEY
        UserDetails userDetails;
        if(isV5User(token.getName())){
            String perId = token.getName().replaceAll(Const.V5_ACCOUNT_KEY, "");
            userDetails = userDetailsService.loadUserByPerId(perId);
        }else {
            //判断是否是广西政务账号过来，如果是则不需要密码登录
            // [2] 使用用户名从数据库读取用户信息
            userDetails = userDetailsService.loadUserByUsername(userName);
        }
        // [3] 检查用户信息
        if(userDetails == null) {
            throw new UsernameNotFoundException("用户不存在");
        } else if (!userDetails.isEnabled()){
            throw new DisabledException("用户已被禁用");
        } else if (!userDetails.isAccountNonExpired()) {
            throw new AccountExpiredException("账号已过期");
        } else if (!userDetails.isAccountNonLocked()) {
            throw new LockedException("账号已被锁定");
        } else if (!userDetails.isCredentialsNonExpired()) {
            throw new LockedException("凭证已过期");
        }
        // [4] 根据不同的情况比对密码
        if (isV5User(token.getName()) || isOAuthUser(token.getName())) {
            // 通过 OAuth 登陆过来的，例如密码是调用 SecurityHelper.login() 前判断是 OAuth 合法登陆的，
            // 然后就查询数据库得到本地用户的密码，这里可以只需要和数据库里的使用等于比较就可以了，具体的仍然需要根据业务逻辑调整
            // 如果不涉及到 OAuth 用户登陆，可以删除此段代码
        } else {
            String encryptedPassword = userDetails.getPassword();   // 数据库用户的密码，一般都是加密过的
            String inputPassword = (String) token.getCredentials(); // 用户输入的密码
            // 根据加密算法加密用户输入的密码，然后和数据库中保存的密码进行比较
            if(!passwordEncoder.matches(inputPassword, encryptedPassword)) {
                throw new BadCredentialsException("用户名/密码无效");
            }
        }
        // [5] 成功登陆，把用户信息提交给 Spring Security
        // 把 userDetails 作为 principal 的好处是可以放自定义的 UserDetails，这样可以存储更多有用的信息，而不只是 username，
        // 默认只有 username，这里的密码使用数据库中保存的密码，而不是用户输入的明文密码，否则就暴露了密码的明文
        return new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities());
    }
    private boolean isV5User(String name) {
        return name.contains(Const.V5_ACCOUNT_KEY);
    }
    @Override
    public boolean supports(Class<?> authentication) {
        return UsernamePasswordAuthenticationToken.class.equals(authentication);
    }
    /**
     * 判断是否通过 OAuth 登陆的用户，例如 gxpss_，广西政务第三方登陆服务。
     *
     * @param userName
     * @return 如果是 OAuth 用户则返回 true，否则返回 false
     */
    private boolean isOAuthUser(String userName) {
        if(userName.contains(Const.USER_ACCOUNT_KEY)){
            return true;
        }else{
            return false;
        }
    }
}
