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

import cn.reghao.jutil.jdk.web.result.Result;
import cn.reghao.jutil.jdk.security.RandomString;
import cn.reghao.tnb.auth.app.model.constant.VerifyChannel;
import cn.reghao.tnb.auth.app.db.mapper.UserAccountMapper;
import cn.reghao.tnb.auth.app.db.repository.AccountRepository;
import cn.reghao.tnb.auth.app.model.dto.PasswordResetDto;
import cn.reghao.tnb.auth.app.model.dto.PasswordUpdateDto;
import cn.reghao.tnb.auth.app.model.dto.UserEmailUpdate;
import cn.reghao.tnb.auth.app.model.po.UserAccount;
import cn.reghao.tnb.auth.app.security.form.AccountAuthToken;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

/**
 * @author reghao
 * @date 2023-02-18 14:42:57
 */
@Service
public class AccountProfileService {
    private final UserAccountMapper userAccountMapper;
    private final AccountRepository accountRepository;
    private final PubkeyService pubkeyService;
    private final CodeService codeService;
    private final AccountTokenService accountTokenService;
    private final PasswordEncoder passwordEncoder;

    public AccountProfileService(UserAccountMapper userAccountMapper, AccountRepository accountRepository,
                                 CodeService codeService, AccountTokenService accountTokenService,
                                 PubkeyService pubkeyService, PasswordEncoder passwordEncoder) {
        this.userAccountMapper = userAccountMapper;
        this.accountRepository = accountRepository;
        this.accountTokenService = accountTokenService;
        this.pubkeyService = pubkeyService;
        this.codeService = codeService;
        this.passwordEncoder = passwordEncoder;
    }

    public Result updateUserScreenName(String screenName) {
        AccountAuthToken authToken = accountTokenService.getAuthToken();
        long userId = authToken.getUserId();
        accountRepository.updateUserScreenName(userId, screenName);
        return Result.success();
    }

    public Result updateUserMobile(UserEmailUpdate emailUpdate) {
        String mobile = emailUpdate.getEmail();
        String smsCode = emailUpdate.getEmailCode();
        String savedVerifyCode = codeService.getVerifyCode(VerifyChannel.passwordChannel.getValue(), mobile);
        if (!savedVerifyCode.equals(smsCode)) {
            return Result.fail("error verify code");
        }

        long loginUser = accountTokenService.getAuthToken().getUserId();
        userAccountMapper.updateUserMobile(loginUser, mobile);
        return Result.success();
    }

    public synchronized Result updateUserEmail(UserEmailUpdate emailUpdate) {
        String emailCode = emailUpdate.getEmailCode();
        String email = emailUpdate.getEmail();
        String savedVerifyCode = codeService.getVerifyCode(VerifyChannel.passwordChannel.getValue(), email);
        if (!savedVerifyCode.equals(emailCode)) {
            return Result.fail("error verify code");
        }

        String password = emailUpdate.getPassword();
        long loginUser = accountTokenService.getAuthToken().getUserId();
        userAccountMapper.updateUserEmail(loginUser, email);
        return Result.success();
    }

    /**
     * 修改密码
     *
     * @param
     * @return
     * @date 2023-02-20 11:48:44
     */
    public Result updatePassword(PasswordUpdateDto passwordUpdateDto) {
        AccountAuthToken authToken = accountTokenService.getAuthToken();
        long userId = authToken.getUserId();

        UserAccount userAccount = accountRepository.getUserAccountByUserId(userId);
        String oldPassword = passwordUpdateDto.getOldPassword();
        String newPassword = passwordUpdateDto.getNewPassword();
        try {
            String decryptOldPassword = pubkeyService.decrypt(oldPassword);
            String salt = userAccount.getSalt();
            String encodedPassword = passwordEncoder.encode(decryptOldPassword + salt);
            if (!encodedPassword.equals(userAccount.getEncodedPassword())) {
                return Result.fail("旧密码不正确");
            }

            String decryptNewPassword = pubkeyService.decrypt(newPassword);
            String newSalt = RandomString.getSalt(64);
            String encodedNewPasswd = passwordEncoder.encode(decryptNewPassword + newSalt);
            userAccountMapper.updateSetPassword(userId, encodedNewPasswd, newSalt);
            accountTokenService.logout(userId);
        } catch (Exception e) {
            return Result.fail(e.getMessage());
        }

        return Result.success("");
    }

    /**
     * 重置密码
     *
     * @param
     * @return
     * @date 2023-02-20 11:48:51
     */
    public Result resetPassword(PasswordResetDto passwordResetDto) {
        try {
            String captchaCode = passwordResetDto.getCaptchaCode();
            String savedCaptchaCode = codeService.getCaptcha();
            if (savedCaptchaCode == null) {
                return Result.fail("当前会话已过期, 请刷新页面后重新登入. 或者检查浏览器是否禁用了 cookie");
            } else if (!captchaCode.equalsIgnoreCase(savedCaptchaCode)) {
                return Result.fail("图形验证码不正确, 请刷新页面后重新获取");
            }

            String email = passwordResetDto.getPrincipal();
            String verifyCode = passwordResetDto.getVerifyCode();
            String savedVerifyCode = codeService.getVerifyCode(VerifyChannel.passwordChannel.getValue(), email);
            if (savedVerifyCode == null) {
                return Result.fail("当前会话已过期, 请刷新页面后重新登入. 或者检查浏览器是否禁用了 cookie");
            } else if (!verifyCode.equalsIgnoreCase(savedVerifyCode)) {
                return Result.fail("短信验证码不正确, 请重新获取");
            }

            UserAccount userAccount = accountRepository.getUserAccountByEmail(email);
            if (userAccount == null) {
                return Result.fail("帐号不存在");
            }

            String newPassword = passwordResetDto.getNewCredential();
            String decryptNewPassword = pubkeyService.decrypt(newPassword);
            String newSalt = RandomString.getSalt(64);
            String encodedNewPasswd = passwordEncoder.encode(decryptNewPassword + newSalt);
            userAccountMapper.updateSetPassword(userAccount.getUserId(), encodedNewPasswd, newSalt);
        } catch (Exception e) {
            return Result.fail(e.getMessage());
        }

        return Result.success();
    }
}
