package ink.wgink.login.base.authentication.user;

import ink.wgink.interfaces.expand.login.ILoginHandlerService;
import ink.wgink.interfaces.user.mongo.IMongoLoginUserService;
import ink.wgink.login.base.consts.IUserCenterConst;
import ink.wgink.login.base.enums.LoginTypeEnum;
import ink.wgink.login.base.exceptions.UserAuthenticationException;
import ink.wgink.login.base.service.user.UserLoginService;
import ink.wgink.pojo.bos.LoginUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;

import javax.servlet.http.HttpSession;

/**
 * When you feel like quitting. Think about why you started
 * 当你想要放弃的时候，想想当初你为何开始
 *
 * @ClassName: UserAuthenticationProvider
 * @Description: 用户认证逻辑
 * @Author: WangGeng
 * @Date: 2020/5/25 1:52 下午
 * @Version: 1.0
 **/
public class UserAuthenticationProvider implements AuthenticationProvider {
    private static final Logger LOG = LoggerFactory.getLogger(UserAuthenticationProvider.class);
    private UserDetailsService userDetailsService;
    private PasswordEncoder passwordEncoder;
    private UserLoginService userLoginService;
    private ILoginHandlerService loginHandler;
    private IMongoLoginUserService mongoLoginUserService;
    private HttpSession httpSession;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        UserAuthenticationToken userAuthenticationToken = (UserAuthenticationToken) authentication;
        String username = userAuthenticationToken.getPrincipal().toString();
        httpSession.setAttribute(IUserCenterConst.LOGIN_USERNAME, username);
        LoginUser loginUser = null;
        if (mongoLoginUserService != null) {
            // mongo 存在，走 MongoDB
            loginUser = mongoLoginUserService.getByUsername(username);
        }
        // 当loginUser为空时，查询数据库，确保用户不存在
        if (loginUser == null) {
            loginUser = (LoginUser) userDetailsService.loadUserByUsername(username);
            // 设置登录用户信息
            userLoginService.setLoginUserInfo(loginUser);
        }
        if (!passwordEncoder.matches(userAuthenticationToken.getCredentials().toString(), loginUser.getPassword())) {
            throw new UserAuthenticationException("用户名或密码错误");
        }
        if (loginUser.getAuthorities().isEmpty()) {
            throw new UserAuthenticationException(loginUser.getUsername() + "用户无任何角色");
        }
        // 清空session中的错误信息
        httpSession.removeAttribute(IUserCenterConst.ERROR_MESSAGE);
        httpSession.removeAttribute(IUserCenterConst.LOGIN_USERNAME);
        // 登录逻辑处理
        loginHandler(loginUser);
        // 更新登录信息
        userLoginService.updateUserLoginInfo(loginUser.getUserId(), loginUser.getUserName(), LoginTypeEnum.USERNAME_AND_PASSWORD.getValue());
        UserAuthenticationToken userAuthenticationTokenResult = new UserAuthenticationToken(loginUser, null, loginUser.getAuthorities());
        userAuthenticationTokenResult.setDetails(userAuthenticationToken.getDetails());
        return userAuthenticationTokenResult;
    }


    /**
     * 登录处理
     *
     * @param loginUser
     */
    private void loginHandler(LoginUser loginUser) {
        if (loginHandler != null) {
            try {
                loginHandler.handle(loginUser);
            } catch (Exception e) {
                LOG.error(e.getMessage(), e);
                throw new UserAuthenticationException("登录异常");
            }
        }
    }

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

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

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

    public void setUserLoginService(UserLoginService userLoginService) {
        this.userLoginService = userLoginService;
    }

    public void setHttpSession(HttpSession httpSession) {
        this.httpSession = httpSession;
    }

    public void setLoginHandler(ILoginHandlerService loginHandler) {
        this.loginHandler = loginHandler;
    }

    public void setMongoLoginUserService(IMongoLoginUserService mongoLoginUserService) {
        this.mongoLoginUserService = mongoLoginUserService;
    }
}
