package com.kxs.user.biz.service.impl;

import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.IdcardUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kxs.common.core.constant.CacheConstants;
import com.kxs.common.core.constant.CommonConstants;
import com.kxs.common.core.constant.SecurityConstants;
import com.kxs.common.core.constant.enums.ErrorTypeEnum;
import com.kxs.common.core.constant.enums.UserStatusEnum;
import com.kxs.common.core.exception.GlobalCustomerException;
import com.kxs.common.core.exception.ValidateCodeException;
import com.kxs.common.core.util.R;
import com.kxs.common.core.util.SpringContextHolder;
import com.kxs.common.security.util.SecurityUtils;
import com.kxs.store.api.feign.RemoteKxsStoreService;
import com.kxs.store.api.model.KxsWarehouseLimit;
import com.kxs.system.api.feign.RemoteUserMsgService;
import com.kxs.system.api.model.KxsUserMsg;
import com.kxs.system.api.util.MsgTemplateUtil;
import com.kxs.user.api.dto.kxsapp.app.ForgetPwdDTO;
import com.kxs.user.api.dto.kxsapp.app.RegisterDTO;
import com.kxs.user.api.dto.kxsapp.user.*;
import com.kxs.user.api.model.*;
import com.kxs.user.api.vo.kxsapp.user.RealUserInfoVO;
import com.kxs.user.api.vo.kxsapp.user.UserCodeInfoVO;
import com.kxs.user.api.vo.kxsapp.user.UserInfoVO;
import com.kxs.user.api.vo.kxsapp.userLevel.AmountDetailVO;
import com.kxs.user.api.vo.kxsapp.userLevel.LevelDetailVO;
import com.kxs.user.api.vo.kxsapp.userLevel.LevelsVO;
import com.kxs.user.biz.constant.UserConstants;
import com.kxs.user.biz.constant.enums.KxsUserStatusEnum;
import com.kxs.user.biz.constant.enums.UserErrorTypeEnum;
import com.kxs.user.biz.constant.enums.UserRealStatusEnum;
import com.kxs.user.biz.mapper.*;
import com.kxs.user.biz.service.KxsUserService;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.CacheManager;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.event.LogoutSuccessEvent;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 用户表(KxsSecurityUser)表服务实现类
 *
 * @author 快秃顶的码农
 * @since 2023-12-05 13:50:24
 */
@Service
@RequiredArgsConstructor
public class KxsUserServiceImpl extends ServiceImpl<KxsUserMapper, KxsUser> implements KxsUserService {

    private final RemoteUserMsgService remoteUserMsgService;

    private final RedisTemplate<String, Object> redisTemplate;

    private final KxsUserAmountMapper userAmountMapper;

    private final OAuth2AuthorizationService authorizationService;

    private final CacheManager cacheManager;

    private final KxsUserRealMapper kxsUserRealMapper;

    private final KxsUserBankMapper kxsUserBankMapper;

    private final RemoteKxsStoreService remoteKxsStoreService;

    private final KxsLeaderMapper kxsLeaderMapper;

    private final KxsPartnerMapper kxsPartnerMapper;

    private final KxsUserPresetLogMapper kxsUserPresetLogMapper;


    private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder();
    
    @Override
    @GlobalTransactional
    @Transactional(rollbackFor = Exception.class)
    public R register(RegisterDTO registerDTO) {
        
        //验证码校验
        checkValidateCode(registerDTO.getPhone(), registerDTO.getCode());

        //查询是否注册
        KxsUser exUser = baseMapper.selectOne(Wrappers.<KxsUser>lambdaQuery().eq(KxsUser::getPhone, registerDTO.getPhone()));
        if(exUser != null){
            return R.failed(UserErrorTypeEnum.PHONE_IS_EX.getDescription());
        }

        KxsUser kxsUser = new KxsUser();
        BeanUtils.copyProperties(registerDTO, kxsUser);
        kxsUser.setUserCode(null);
        kxsUser.setStatus(UserStatusEnum.NORMAL.getCode());

        //查询上级用户
        KxsUser inviteUser = baseMapper.selectOne(Wrappers.<KxsUser>lambdaQuery().eq(KxsUser::getUserCode, registerDTO.getUserCode()));
        if(inviteUser == null){
            inviteUser = new KxsUser();
            inviteUser.setId(0);
//            return R.failed(UserErrorTypeEnum.PARENT_IS_NULL.getDescription());
        }

        kxsUser.setPid(inviteUser.getId());
        //加盐
        String salt = UUID.randomUUID().toString();
        kxsUser.setSalt(salt);
        //登陆密码
        kxsUser.setPassword(ENCODER.encode(registerDTO.getPassword() + salt));
        //保存用户注册信息
        baseMapper.insert(kxsUser);

        //会员序号/推荐码
        String number = String.format("%08d", kxsUser.getId());
        kxsUser.setUserCode(CommonConstants.KXS_USER_NUMBER_PREFIX + number);

        if (CharSequenceUtil.isBlank(inviteUser.getPidPath())){
            kxsUser.setPidPath("0");
        } else {
            //  拼接上级推荐人的用户ID字符串；如：0,1,2,3,4,5,...用英文逗号隔开
            String superIdTree = (inviteUser.getPidPath().length() == 1 ? inviteUser.getPidPath() + "," : inviteUser.getPidPath()) + inviteUser.getId() + ",";
            kxsUser.setPidPath(superIdTree);
        }
        //更新
        baseMapper.updateById(kxsUser);

        //添加用户账户表
        KxsUserAmount kxsUserAmount = new KxsUserAmount();
        kxsUserAmount.setUserId(kxsUser.getId());
        userAmountMapper.insert(kxsUserAmount);

        //初始化用户预发额度表
        KxsWarehouseLimit kxsWarehouseLimit = new KxsWarehouseLimit();
        kxsWarehouseLimit.setUserId(kxsUser.getId());
        R<Boolean> booleanR = remoteKxsStoreService.addUserAdvanceLimit(kxsWarehouseLimit, SecurityConstants.FROM_IN);
        if(!booleanR.getData()){
            throw new GlobalCustomerException(UserErrorTypeEnum.USER_LIMIT_ERROR.getDescription());
        }

        //发送个人消息
        KxsUserMsg kxsUserMsg = MsgTemplateUtil.newUserPush(inviteUser.getId());
        remoteUserMsgService.pushUserMsg(kxsUserMsg, SecurityConstants.FROM_IN);

        return R.ok();
        
    }

    @Override
    public Boolean sendCode(String mobile) {

        String key = CacheConstants.DEFAULT_CODE_KEY + mobile;

        Object codeObj = redisTemplate.opsForValue().get(key);

        if (ObjectUtil.isNotEmpty(codeObj)) {
            throw new ValidateCodeException(UserErrorTypeEnum.CODE_NOT_EX.getDescription());
        }

        String code = "111111"; //RandomUtil.randomNumbers(Integer.parseInt(UserConstants.CODE_SIZE));
        redisTemplate.opsForValue().set(key, code, UserConstants.CODE_TIME, TimeUnit.SECONDS);

        return Boolean.TRUE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean forgetPwd(ForgetPwdDTO param) {
        //校验验证码
        checkValidateCode(param.getPhone(), param.getCode());

        //查询用户
        KxsUser user = this.getOne(Wrappers.<KxsUser>lambdaQuery().eq(KxsUser::getPhone, param.getPhone()));
        if(user == null){
            throw new GlobalCustomerException(UserErrorTypeEnum.USER_NOT_EXIST.getDescription());
        }

        if(CharSequenceUtil.isBlank(user.getSalt())){
            user.setSalt(UUID.randomUUID().toString());
        }
        //检查重复密码
        if(!Objects.equals(param.getPassword(), param.getConfirmPwd())){
            throw new GlobalCustomerException(UserErrorTypeEnum.PWD_NOT_EQUALS.getDescription());
        }

        //重置密码
        user.setPassword(ENCODER.encode(param.getPassword() + user.getSalt()));

        //保存新密码
        this.updateById(user);

        return Boolean.TRUE;
    }

    @Override
    public Boolean existPayPwd() {

        KxsUser user = this.getById(SecurityUtils.getUser().getId());
        if(CharSequenceUtil.isBlank(user.getPayPwd())){
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    @Override
    public Boolean checkPayPwd(String payPwd) {

        KxsUser user = this.getById(SecurityUtils.getUser().getId());

        if(!Objects.equals(user.getPayPwd(), SecureUtil.md5(payPwd + user.getSalt()))){
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    @Override
    public UserCodeInfoVO userCodeInfo(String userCode) {

        UserCodeInfoVO userCodeInfoVO = new UserCodeInfoVO();

        KxsUser user = this.getOne(Wrappers.<KxsUser>lambdaQuery().eq(KxsUser::getUserCode, userCode));
        if(user == null) {
            return userCodeInfoVO;
        }
        BeanUtils.copyProperties(user, userCodeInfoVO);

        return userCodeInfoVO;
    }

    @Override
    public Boolean checkHandPwd(String handPwd) {

        KxsUser user = this.getById(SecurityUtils.getUser().getId());

        if(!Objects.equals(user.getHandPwd(), SecureUtil.md5(handPwd + user.getSalt()))){
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    @Override
    public Boolean checkLoginPwd(String password) {

        KxsUser user = this.getById(SecurityUtils.getUser().getId());

        if(!ENCODER.matches(password + user.getSalt(), user.getPassword())){
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    @Override
    public Boolean logout(String tokenValue) {

        OAuth2Authorization authorization = authorizationService.findByToken(tokenValue, OAuth2TokenType.ACCESS_TOKEN);
        if (authorization == null) {
            return Boolean.TRUE;
        }

        OAuth2Authorization.Token<OAuth2AccessToken> accessToken = authorization.getAccessToken();
        if (accessToken == null || StrUtil.isBlank(accessToken.getToken().getTokenValue())) {
            return Boolean.TRUE;
        }
        // 清空用户信息（立即删除）
        Objects.requireNonNull(cacheManager.getCache(CacheConstants.USER_DETAILS)).evictIfPresent(authorization.getPrincipalName());
        // 清空access token
        authorizationService.remove(authorization);
        // 处理自定义退出事件，保存相关日志
        SpringContextHolder.publishEvent(new LogoutSuccessEvent(new PreAuthenticatedAuthenticationToken(
                authorization.getPrincipalName(), authorization.getRegisteredClientId())));

        return Boolean.TRUE;
    }

    @Override
    public UserInfoVO info() {

        KxsUser user = this.getById(SecurityUtils.getUser().getId());

        KxsUser inviteUser = this.getOne(Wrappers.<KxsUser>lambdaQuery().eq(KxsUser::getId, user.getPid()));

        KxsUserReal userReal = kxsUserRealMapper.selectOne(Wrappers.<KxsUserReal>lambdaQuery().eq(KxsUserReal::getUserId, user.getId()));

        UserInfoVO userInfoVO = new UserInfoVO();
        BeanUtils.copyProperties(user, userInfoVO);

        if(inviteUser != null){
            userInfoVO.setInviteMobile(inviteUser.getPhone());
            userInfoVO.setInviteName(inviteUser.getUsername());
        }
        if(userReal != null){
            userInfoVO.setRealTime(userReal.getCreateTime());
        }

        return userInfoVO;
    }

    @Override
    public R updateArea(UserUpdateAreaDTO param) {

        KxsUser user = this.getById(SecurityUtils.getUser().getId());
        user.setAddress(param.getAddress());
        this.update(Wrappers.<KxsUser>lambdaUpdate().eq(KxsUser::getId, user.getId()).set(KxsUser::getAddress, param.getAddress()));

        return R.ok();

    }

    @Override
    public R modifyPayPwd(KxsUser param) {

        KxsUser user = this.getById(SecurityUtils.getUser().getId());
        user.setPayPwd(SecureUtil.md5(param.getPayPwd() + user.getSalt()));

        this.update(Wrappers.<KxsUser>lambdaUpdate().eq(KxsUser::getId, user.getId()).set(KxsUser::getPayPwd, user.getPayPwd()));

        return R.ok();
    }

    @Override
    public R updatePwd(UserUpdatePwdDTO param) {

        if(!Objects.equals(param.getNewPwd(), param.getConfirmPwd())){
            return R.failed(UserErrorTypeEnum.PWD_NOT_EQUALS.getDescription());
        }

        KxsUser user = this.getById(SecurityUtils.getUser().getId());

        if(!ENCODER.matches(param.getPassword() + user.getSalt(), user.getPassword())){
            return R.failed(UserErrorTypeEnum.PWD_ERROR.getDescription());
        }

        this.update(Wrappers.<KxsUser>lambdaUpdate().eq(KxsUser::getId, user.getId()).set(KxsUser::getPassword, ENCODER.encode(param.getNewPwd() + user.getSalt())));

        return R.ok();
    }

    @Override
    public R updateLoginPhone(UserUpdateLoginPhoneDTO param) {

        KxsUser user = this.getById(SecurityUtils.getUser().getId());
        //判断手机号
        if (!Objects.equals(user.getPhone(), param.getPhone())){
            return R.failed(UserErrorTypeEnum.PHONE_IS_INCONSISTENT.getDescription());
        }
        //判断密码
        if(!ENCODER.matches(param.getPassword() + user.getSalt(), user.getPassword())){
            return R.failed(UserErrorTypeEnum.PWD_ERROR.getDescription());
        }

        user.setPhone(param.getNewPhone());
        this.update(Wrappers.<KxsUser>lambdaUpdate().eq(KxsUser::getId, user.getId()).set(KxsUser::getPhone, param.getNewPhone()));

        return R.ok();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R realUser(RealUserDTO param) {

        if(!IdcardUtil.isValidCard(param.getIdCard())){
            return R.failed(UserErrorTypeEnum.ID_CARD_ERROR.getDescription());
        }
        Long userId = SecurityUtils.getUser().getId();
        KxsUser user = this.getById(userId);
        //查询实名信息
        KxsUserReal userReal = kxsUserRealMapper.selectOne(Wrappers.<KxsUserReal>lambdaQuery().eq(KxsUserReal::getUserId, user.getId()));
        if(userReal != null){
            userReal.setRealName(param.getRealName());
            userReal.setIdCard(param.getIdCard());
            userReal.setIdCardFront(param.getIdCardFront());
            userReal.setIdCardHand(param.getIdCardHand());
            userReal.setIdCardRear(param.getIdCardRear());
            kxsUserRealMapper.updateById(userReal);
        }else{
            userReal = new KxsUserReal();
            BeanUtils.copyProperties(param, userReal);
            userReal.setUserId(userId.intValue());
            kxsUserRealMapper.insert(userReal);
        }
        //查询银行卡信息
        KxsUserBank userBank = kxsUserBankMapper.selectOne(Wrappers.<KxsUserBank>lambdaQuery().eq(KxsUserBank::getUserId, user.getId()));
        if (userBank != null){
            userBank.setBankPic(param.getBankPic());
            userBank.setBankOpen(param.getBankOpen());
            userBank.setBankCode(param.getBankCode());
            userBank.setBankPhone(param.getBankPhone());
            userBank.setBankName(param.getBankName());
            kxsUserBankMapper.updateById(userBank);
        }else{
            userBank = new KxsUserBank();
            BeanUtils.copyProperties(param, userBank);
            userBank.setUserId(userId.intValue());
            kxsUserBankMapper.insert(userBank);
        }

        this.update(Wrappers.<KxsUser>lambdaUpdate()
                .eq(KxsUser::getId, user.getId())
                .set(KxsUser::getUsername, param.getRealName())
                .set(KxsUser::getRealStatus, UserRealStatusEnum.YES.getType()));

        return R.ok();
    }

    @Override
    public RealUserInfoVO getRealUserInfo() {

        return baseMapper.findUserRealBankInfo(SecurityUtils.getUser().getId().intValue());
    }

    @Override
    public R handPwd(UserHandPwdDTO param) {

        if(!Objects.equals(param.getHandPwd(), param.getConfirmPwd())){
            return R.failed(UserErrorTypeEnum.PWD_NOT_EQUALS.getDescription());
        }

        KxsUser user = this.getById(SecurityUtils.getUser().getId());
        String handPwd = SecureUtil.md5(param.getHandPwd() + user.getSalt());

        this.update(Wrappers.<KxsUser>lambdaUpdate().eq(KxsUser::getId, user.getId()).set(KxsUser::getHandPwd, handPwd));

        return R.ok();
    }

    @Override
    public Boolean checkUserInfo(UserCheckUserInfoDTO param) {

        checkValidateCode(param.getPhone(), param.getCode());

        KxsUserReal userReal = kxsUserRealMapper.selectOne(Wrappers.<KxsUserReal>lambdaQuery().eq(KxsUserReal::getUserId, SecurityUtils.getUser().getId()));

        if(!param.getRealName().equals(userReal.getRealName()) || !param.getIdCard().equals(userReal.getIdCard())){
            return Boolean.FALSE;
        }

        return Boolean.TRUE;
    }

    @Override
    public Boolean updateHead(KxsUser param) {

        return this.update(Wrappers.<KxsUser>lambdaUpdate()
                .eq(KxsUser::getId, SecurityUtils.getUser().getId())
                .set(KxsUser::getAvatar, param.getAvatar()));
    }

    @Override
    public Boolean removeUser() {

        return this.update(Wrappers.<KxsUser>lambdaUpdate()
                .eq(KxsUser::getId, SecurityUtils.getUser().getId())
                .set(KxsUser::getStatus, KxsUserStatusEnum.REMOVE.getType()));
    }

    @Override
    public Boolean checkUserIsChildren(Integer userId, Integer checkUserId) {

        KxsUser user = baseMapper.selectById(checkUserId);
        String pidPath = user.getPidPath();
        //判断是否是下级
        boolean contains = pidPath.contains("," + userId + ",");
        if(!contains){
            return Boolean.FALSE;
        }

        return Boolean.TRUE;
    }

    @Override
    public RealUserInfoVO loadUserRealBankById(Integer userId) {

        return baseMapper.findUserRealBankInfo(userId);
    }

    @Override
    public LevelDetailVO levelDetail() {

        KxsUser user = this.getById(SecurityUtils.getUser().getId());

        LevelDetailVO levelDetailVO = new LevelDetailVO();
        levelDetailVO.setId(user.getId());
        levelDetailVO.setUsername(user.getUsername());
        levelDetailVO.setAvatar(user.getAvatar());
        //上月业绩等级
        levelDetailVO.setLastLevel(user.getUserLevel());
        //查询用户最高职级
        Integer level = baseMapper.selectMaxLevel(SecurityUtils.getUser().getId());
        levelDetailVO.setUserLevel(level);

        //查询各个职级列表
        ArrayList<LevelsVO> levels = new ArrayList<>();

        //合伙人职级
        KxsPartner kxsPartner = kxsPartnerMapper.selectOne(Wrappers.<KxsPartner>lambdaQuery().eq(KxsPartner::getUserId, user.getId()));
        if(kxsPartner != null){
            LevelsVO levelsVO = new LevelsVO();
            levelsVO.setLevelName("合伙人职级");
            levelsVO.setLevel(kxsPartner.getPartnerLevel());
            levelsVO.setExpiredDate(kxsPartner.getExpiredDate());
            if(kxsPartner.getExpiredDate().isBefore(LocalDateTime.now())){
                levelsVO.setStatus(0);
            }else{
                levelsVO.setStatus(1);
            }
            levels.add(levelsVO);
        }
        //盟主职级
        KxsLeader kxsLeader = kxsLeaderMapper.selectOne(Wrappers.<KxsLeader>lambdaQuery().eq(KxsLeader::getUserId, user.getId()));
        if(kxsLeader != null){
            LevelsVO levelsVO = new LevelsVO();
            levelsVO.setLevelName("盟主职级");
            levelsVO.setLevel(kxsLeader.getLeaderLevel());
            levelsVO.setExpiredDate(kxsLeader.getExpiredDate());
            if(kxsLeader.getExpiredDate().isBefore(LocalDateTime.now())){
                levelsVO.setStatus(0);
            }else{
                levelsVO.setStatus(1);
            }
            levels.add(levelsVO);
        }
        //预设职级
        KxsUserPresetLog userPresetLog = kxsUserPresetLogMapper.selectOne(Wrappers.<KxsUserPresetLog>lambdaQuery().eq(KxsUserPresetLog::getUserId, user.getId()));
        if(userPresetLog != null){
            LevelsVO levelsVO = new LevelsVO();
            levelsVO.setLevelName("预设职级");
            levelsVO.setLevel(userPresetLog.getPresetLevel());
            levelsVO.setExpiredDate(userPresetLog.getEndTime());
            if(userPresetLog.getEndTime().isBefore(LocalDateTime.now())){
                levelsVO.setStatus(0);
            }else{
                levelsVO.setStatus(1);
            }
            levels.add(levelsVO);
        }
        levelDetailVO.setLevels(levels);

        return levelDetailVO;
    }



    public static void main(String[] args) {
        System.out.println(ENCODER.encode("aa121314" +"abc108fa-2060-46fc-b21b-fd5246fecb86"));
        System.out.println(ENCODER.matches("aa121314" +"abc108fa-2060-46fc-b21b-fd5246fecb86", "$2a$10$sBtcVoeOuemMvhVI.OIp3e9LNlamWTOglZdf1AcPIJRY1KKNUvrOW"));
    }


    /**
     * 检查验证码
     *
     * @param mobile 手机号码
     * @param code   密码
     */
    private void checkValidateCode(String mobile, String code){

        if (CharSequenceUtil.isBlank(code)) {
            throw new ValidateCodeException("验证码不能为空");
        }

        String key = CacheConstants.DEFAULT_CODE_KEY + mobile;

        Object codeObj = redisTemplate.opsForValue().get(key);

        if (ObjectUtil.isEmpty(codeObj) || !Objects.equals(code, codeObj)) {
            throw new ValidateCodeException(ErrorTypeEnum.VERIFICATION_CODE_IS_ILLEGAL.getDescription());
        }

        redisTemplate.delete(key);
    }

}

