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

import cn.reghao.jutil.jdk.web.result.Result;
import cn.reghao.jutil.jdk.web.result.ResultStatus;
import cn.reghao.jutil.jdk.security.RandomString;
import cn.reghao.tnb.common.util.StringRegexp;
import cn.reghao.tnb.auth.app.model.constant.VerifyChannel;
import cn.reghao.tnb.auth.app.model.dto.AccountRegisterDto;
import cn.reghao.tnb.auth.app.db.repository.AccountRepository;
import cn.reghao.tnb.auth.app.model.po.UserAuthority;
import cn.reghao.tnb.auth.app.model.po.UserRegistry;
import cn.reghao.tnb.auth.app.service.CodeService;
import cn.reghao.tnb.auth.app.model.po.UserAccount;
import cn.reghao.tnb.auth.app.service.AccountRegistryService;
import cn.reghao.tnb.auth.app.service.PubkeyService;
import cn.reghao.tnb.common.auth.AccountRole;
import cn.reghao.tnb.common.util.ConstantId;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.Set;

/**
 * @author reghao
 * @date 2022-04-13 10:15:34
 */
@Slf4j
@Service
public class AccountRegistryServiceImpl implements AccountRegistryService {
    private final CodeService codeService;
    private final PasswordEncoder passwordEncoder;
    private final AccountRepository accountRepository;
    private final PubkeyService pubkeyService;

    public AccountRegistryServiceImpl(CodeService codeService, PasswordEncoder passwordEncoder,
                                      AccountRepository accountRepository, PubkeyService pubkeyService) {
        this.codeService = codeService;
        this.passwordEncoder = passwordEncoder;
        this.accountRepository = accountRepository;
        this.pubkeyService = pubkeyService;
    }

    @Override
    public UserRegistry getUserRegistry() {
        return accountRepository.getUserRegistry();
    }

    @Override
    public synchronized Result createAccount(AccountRegisterDto accountRegisterDto) {
        UserRegistry userRegistry = accountRepository.getUserRegistry();
        if (!userRegistry.getEnabled()) {
            return Result.fail("系统当前未开放注册");
        }

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

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

        String credential = accountRegisterDto.getCredential();
        String decryptCredential;
        try {
            decryptCredential = pubkeyService.decrypt(credential);
        } catch (Exception e) {
            return Result.result(ResultStatus.FAIL, "公钥不正确, 请刷新页面后重新注册");
        }

        if (!StringRegexp.matchPassword(decryptCredential)) {
            return Result.result(ResultStatus.FAIL, "密码格式不正确, 密码必须包含大写字母, 数字, 特殊字符");
        }

        long userId = accountRepository.getNextUserId();
        String salt = RandomString.getSalt(64);
        String encodedPassword = passwordEncoder.encode(decryptCredential + salt);
        Set<UserAuthority> authorities = Set.of(new UserAuthority());
        if (StringRegexp.matchMobile(principal)) {
            UserAccount userAccount = new UserAccount(principal, userId, encodedPassword, salt, authorities);
            accountRepository.saveAccount(userAccount);
            return Result.result(ResultStatus.SUCCESS, "帐号已创建");
        }
        throw new UsernameNotFoundException(String.format("%s is not mobile number", principal));
    }

    @Override
    public UserAccount createAccount(String principal, String password) {
        long userId = accountRepository.getNextUserId();
        if (StringRegexp.matchMobile(principal)) {
            UserAccount userAccount = new UserAccount(principal, userId);
            accountRepository.saveAccount(userAccount);
            return accountRepository.getUserAccountByMobile(principal);
        }
        throw new UsernameNotFoundException(String.format("%s is not mobile number", principal));
    }

    @Override
    public void createAdminAccount() {
        long userId = accountRepository.getNextUserId();
        if (userId == ConstantId.START_USER_ID) {
            String principal = ConstantId.ADMIN_MOBILE;
            String password = ConstantId.ADMIN_PASSWORD;
            String salt = RandomString.getSalt(64);
            String encodedPassword = passwordEncoder.encode(password + salt);
            Set<UserAuthority> authorities = Set.of(new UserAuthority(AccountRole.admin.getValue()));

            UserAccount userAccount = new UserAccount(principal, userId, encodedPassword, salt, authorities);
            accountRepository.saveAccount(userAccount);
            log.info("admin 帐号已创建");
        } else {
            log.info("admin 帐号已存在");
        }
    }
}
