package cn.reghao.tnb.auth.app.service.impl;

import cn.reghao.tnb.auth.app.model.constant.VerifyChannel;
import cn.reghao.tnb.auth.app.db.repository.AccountRepository;
import cn.reghao.tnb.auth.app.model.dto.AccountLoginDto;
import cn.reghao.tnb.auth.app.model.po.UserAccount;
import cn.reghao.tnb.auth.app.service.*;
import cn.reghao.tnb.auth.app.security.exception.AccountLoginException;
import cn.reghao.tnb.auth.app.security.form.AccountAuthToken;
import cn.reghao.tnb.common.util.StringRegexp;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException;
import org.springframework.stereotype.Service;

import java.util.UUID;

/**
 * @author reghao
 * @date 2022-07-08 14:51:17
 */
@Service
public class AccountLoginServiceImpl implements AccountLoginService {
    private final AccountRepository accountRepository;
    private final CodeService codeService;
    private final PubkeyService pubkeyService;
    private final AccountRegistryService accountRegistryService;
    private final LoginAttemptService loginAttemptService;
    private final PasswordEncoder passwordEncoder;

    public AccountLoginServiceImpl(AccountRepository accountRepository, CodeService codeService, PubkeyService pubkeyService,
                                   AccountRegistryService accountRegistryService, LoginAttemptService loginAttemptService,
                                   PasswordEncoder passwordEncoder) {
        this.accountRepository = accountRepository;
        this.codeService = codeService;
        this.pubkeyService = pubkeyService;
        this.accountRegistryService = accountRegistryService;
        this.loginAttemptService = loginAttemptService;
        this.passwordEncoder = passwordEncoder;
    }

    @Override
    public AccountAuthToken getPreAuthentication(AccountLoginDto userLoginDto) {
        Integer loginType = userLoginDto.getLoginType();
        Integer plat = userLoginDto.getPlat();
        String principal = userLoginDto.getPrincipal();
        String credential = userLoginDto.getCredential();
        String captchaCode = userLoginDto.getCaptchaCode();
        Boolean rememberMe = userLoginDto.getRememberMe();

        String savedCaptchaCode = codeService.getCaptcha();
        if (savedCaptchaCode == null) {
            String errMsg = "当前会话已过期, 请刷新页面后重新登入. 或者检查浏览器是否禁用了 cookie";
            throw new PreAuthenticatedCredentialsNotFoundException(errMsg);
        } else if (!savedCaptchaCode.equalsIgnoreCase(captchaCode)) {
            String errMsg = "图形验证码不正确...";
            throw new PreAuthenticatedCredentialsNotFoundException(errMsg);
        }

        String decryptCredential;
        try {
            decryptCredential = pubkeyService.decrypt(credential);
        } catch (Exception e) {
            throw new PreAuthenticatedCredentialsNotFoundException(e.getMessage());
        }

        String loginId = UUID.randomUUID().toString().replace("-", "");
        return new AccountAuthToken(plat, loginId, loginType, rememberMe, principal, decryptCredential);
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserAccount userAccount;
        if (StringRegexp.matchEmail(username)) {
            userAccount = accountRepository.getUserAccountByEmail(username);
        } else if (StringRegexp.matchMobile(username)) {
            userAccount = accountRepository.getUserAccountByMobile(username);
        } else {
            userAccount = accountRepository.getUserAccountByUsername(username);
        }

        if (userAccount == null) {
            String errMsg = String.format("帐号 %s 未注册", username);
            throw new UsernameNotFoundException(errMsg);
        }
        return userAccount;
    }

    @Override
    public AccountAuthToken authByPassword(AccountAuthToken authToken) {
        String username = (String) authToken.getPrincipal();
        UserAccount userDetail = (UserAccount) loadUserByUsername(username);
        long userId = userDetail.getUserId();
        String loginId = authToken.getLoginId();
        loginAttemptService.loginCheck(userId);

        String password = (String) authToken.getCredentials();
        String encodedPassword = passwordEncoder.encode(password + userDetail.getSalt());
        if (!userDetail.getPassword().equals(encodedPassword)) {
            String errMsg = "账号或密码不正确";
            throw new AccountLoginException(errMsg, userId, loginId);
        }

        //userDetail.setLoginType(LoginType.usernamePassword.getValue());
        return getAccountAuthToken(authToken, userDetail);
    }

    /**
     * TODO 手机号验证码登入, 手机号不存在则自动注册
     *
     * @param
     * @return
     * @date 2022-07-08 下午3:11
     */
    @Override
    public AccountAuthToken authByVerifyCode(AccountAuthToken authToken) {
        int plat = authToken.getPlat();
        String principal = (String) authToken.getPrincipal();
        String credential = (String) authToken.getCredentials();

        UserAccount userDetail;
        try {
            userDetail = (UserAccount) loadUserByUsername(principal);
            String savedCredential = codeService.getVerifyCode(VerifyChannel.loginChannel.getValue(), principal);
            if (savedCredential == null) {
                String errMsg = "当前会话已过期, 请刷新页面后重新登入. 或者检查浏览器是否禁用了 cookie";
                throw new DisabledException(errMsg);
            } else if (!credential.equals(savedCredential)) {
                String errMsg = "短信验证码不正确";
                if (userDetail != null) {
                    long userId = userDetail.getUserId();
                    String loginId = authToken.getLoginId();
                    loginAttemptService.loginCheck(userId);
                    throw new AccountLoginException(errMsg, userId, loginId);
                }

                throw new DisabledException(errMsg);
            }
        } catch (UsernameNotFoundException e) {
            // 验证码方式登入时帐号不存在则自动注册
            userDetail = accountRegistryService.createAccount(principal, "");
        }

        //userDetail.setLoginType(LoginType.mobileCode.getValue());
        return getAccountAuthToken(authToken, userDetail);
    }

    private AccountAuthToken getAccountAuthToken(AccountAuthToken authToken, UserAccount userDetail) {
        AccountAuthToken accountAuthToken = new AccountAuthToken(authToken, userDetail);
        loginAttemptService.saveLoginAttempts(accountAuthToken);
        return accountAuthToken;
    }
}
