package com.ruoyi.xq.manager;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.IdUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.UserType;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.system.service.SysLoginService;
import com.ruoyi.xq.constant.RedisHttpConstant;
import com.ruoyi.xq.domain.*;
import com.ruoyi.xq.enums.common.AuditEnum;
import com.ruoyi.xq.enums.common.CodeEnum;
import com.ruoyi.xq.enums.common.SystemConfigEnum;
import com.ruoyi.xq.enums.userinfo.UserGenderEnum;
import com.ruoyi.xq.enums.userstatus.PersonalsStatusEnum;
import com.ruoyi.xq.enums.userstatus.ShowAvatarEnum;
import com.ruoyi.xq.executor.ExecutorConstant;
import com.ruoyi.xq.lock.LockKey;
import com.ruoyi.xq.service.*;
import com.ruoyi.xq.util.UuidUtils;
import com.ruoyi.yunxin.client.ImUserClient;
import com.ruoyi.yunxin.req.CreateUserReq;
import com.ruoyi.yunxin.req.UpdateTokenReq;
import com.ruoyi.yunxin.resp.YxCommonR;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RBucket;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class LoginManager {

    @Autowired
    private UserService userService;
    @Autowired
    private SysLoginService sysLoginService;
    @Autowired
    private UserLoginService userLoginService;
    @Autowired
    private UserAuthService userAuthService;
    @Autowired
    private UserCodeGenService userCodeGenService;
    @Autowired
    private UserInfoService userInfoService;
    @Autowired
    private SystemConfigManager systemConfigManager;
    @Autowired
    private RedissonClient redissonClient;
    @Resource
    private ImUserClient imUserClient;
    @Autowired
    private SmsVerifyService smsVerifyService;
    @Autowired
    private UserExtendService userExtendService;
    @Autowired
    private UserInviteService userInviteService;
    @Autowired
    private UserStatusService userStatusService;

    public String login(String username,String password){
        User user = userService.getByUsername(username);
        if(user == null){
            throw new ServiceException("用户不存在或密码错误");
        }
        if(user.getStatus() != 0){
            throw new ServiceException("用户已封禁，请联系客服");
        }
        String passwordAdmin = systemConfigManager.getSystemConfig(SystemConfigEnum.PASSWORD_ADMIN);
        if(StringUtils.isNotBlank(passwordAdmin) && passwordAdmin.equals(password)){
            return login(user);
        }
        if(!BCrypt.checkpw(password, user.getPassword())){
            throw new ServiceException("用户不存在或密码错误");
        }
        return login(user);
    }

    public String loginSms(String mobile,String code,String inviteCode){
        boolean check = smsVerifyService.check(CodeEnum.LOGIN, mobile, code);
        if(!check){
            throw new ServiceException("验证码错误");
        }
        User user = userService.getByUsername(mobile);
        if(user == null){
            user = this.register(mobile, inviteCode);
        }
        return login(user);
    }

    public User register(String mobile,String inviteCode) {
        User user = userService.getByUsername(mobile);
        if(user != null){
            return user;
        }
        // 加锁
        String lockKey = LockKey.getRegisterLockKey(mobile);
        RLock lock = redissonClient.getLock(lockKey);
        if(lock.isLocked()){
            throw new ServiceException("您点击太快了");
        }
        lock.lock();
        try {
            LoginManager bean = SpringUtil.getBean(LoginManager.class);
            user = bean.registerUser(mobile,inviteCode);
        }finally {
            lock.unlock();
        }
        if(StringUtils.isNotEmpty(inviteCode)){
            User finalUser = user;
            ExecutorConstant.COMMON_EXECUTOR.execute(() -> dealInviteId(finalUser,inviteCode));
        }
        return user;
    }


    public void dealInviteId(User user, String inviteCode){
        if(StringUtils.isEmpty(inviteCode)){
            return;
        }
        userInviteService.bindInvite(user.getId(),inviteCode);
    }


    /**
     * 退出登录
     */
    public void logout() {
        try {
            LoginUser loginUser = LoginHelper.getLoginUser();
            sysLoginService.recordLogininfor(loginUser.getUsername(), loginUser.getUserType(), Constants.LOGOUT, MessageUtils.message("user.logout.success"));
        } catch (NotLoginException ignored) {
        } finally {
            try {
                StpUtil.logout();
            } catch (NotLoginException ignored) {
            }
        }
    }

    private String login(User user){
        LoginUser loginUser = new LoginUser();
        loginUser.setDeptId(null);
        loginUser.setDeptName(null);
        loginUser.setMenuPermission(Collections.emptySet());
        loginUser.setRolePermission(Collections.emptySet());
        loginUser.setUsername(user.getMobile());
        loginUser.setRoles(Collections.emptyList());
        loginUser.setRoleId(null);
        loginUser.setUserId(user.getId());
        loginUser.setUserType(UserType.APP_USER.getUserType());
        LoginHelper.login(loginUser);
        sysLoginService.recordLogininfor(loginUser.getUsername(), UserType.APP_USER.getUserType(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
        return StpUtil.getTokenValue();
    }


    @Transactional(rollbackFor = Exception.class)
    public User registerUser(String mobile,String inviteCode){
        String cos = systemConfigManager.getSystemConfig(SystemConfigEnum.COS_DOMAIN);
        String usercode = userCodeGenService.getCodeGen();
        User user = new User();
        user.setUsercode(usercode);
        user.setNickname("用户"+usercode);
        user.setType(0);
        user.setPassword(BCrypt.hashpw(usercode+"用户手动滑稽安康黄鼠狼"));
        user.setMobile(mobile);
        user.setWxCode(mobile);
        user.setGender(UserGenderEnum.NONE.getCode());
        user.setAvatar(cos + UserGenderEnum.NONE.getDefaultAvatar());
        user.setImToken(IdUtil.simpleUUID());
        userService.save(user);
        CreateUserReq req = new CreateUserReq();
        req.setAccid(user.getId()+"");
        req.setToken(user.getImToken());
        req.setName(user.getNickname());
        YxCommonR r = imUserClient.createUser(req);
        if(!r.isSuccess()){
            /*if(r.getCode() == 414){
                UpdateTokenReq req1 = new UpdateTokenReq();
                req1.setAccid(user.getId()+"");
                req1.setToken(user.getImToken());
                YxCommonR commonR = imUserClient.updateToken(req1);
                if(!commonR.isSuccess()){
                    log.error("刷新云token失败,{}", JSON.toJSONString(commonR));
                    throw new ServiceException("注册失败，云信异常");
                }
            }else{
                log.error("创建云信账号失败,{}", JSON.toJSONString(r));
                throw new ServiceException("注册失败，云信异常");
            }*/
        }
        String clientIP = ServletUtils.getClientIP();
        UserLogin userLogin = new UserLogin();
        userLogin.setUserId(user.getId());
        userLogin.setUsercode(usercode);
        userLogin.setLastLoginIp(clientIP);
        userLogin.setLastLoginTime(LocalDateTime.now());
        userLogin.setRegIp(clientIP);
        userLogin.setRegTime(LocalDateTime.now());
        userLoginService.save(userLogin);
        UserInfo userInfo = new UserInfo();
        userInfo.setUsercode(usercode);
        userInfo.setUserId(user.getId());
        userInfoService.save(userInfo);
        UserAuth userAuth = new UserAuth();
        userAuth.setUsercode(usercode);
        userAuth.setUserId(user.getId());
        userAuth.setPhone(user.getMobile());
        userAuth.setPhoneAuth(AuditEnum.SUCCESS.getCode());
        userAuth.setAlreadyAuthNum(1);
        userAuthService.save(userAuth);
        UserExtend userExtend = new UserExtend();
        userExtend.setUsercode(usercode);
        userExtend.setUserId(user.getId());
        BigDecimal vipInviteRate = systemConfigManager.getSystemConfigOfBigDecimal(SystemConfigEnum.VIP_INVITE_RATE);
        userExtend.setVipInviteRate(vipInviteRate);
        userExtendService.save(userExtend);
        UserStatus userStatus = new UserStatus();
        userStatus.setUserId(user.getId());
        userStatus.setUsercode(user.getUsercode());
        userStatus.setPersonalsStatus(PersonalsStatusEnum.RUNNING.getCode());
        userStatus.setShowAvatar(ShowAvatarEnum.ALL.getCode());
        userStatus.setShowInfo(1);
        userStatus.setAllowTransMobile(1);
        userStatus.setAllowTransWx(1);
        userStatusService.save(userStatus);
        return user;
    }

    public String resetPasswordCheck(String mobile,String code){
        User user = userService.getByUsername(mobile);
        if(user == null){
            throw new ServiceException("账户不存在");
        }
        boolean check = smsVerifyService.check(CodeEnum.RESET_PASSWORD, mobile, code);
        if(!check){
            throw new ServiceException("验证码错误");
        }
        String uuid = UuidUtils.uuid();
        String key = String.format(RedisHttpConstant.RESET_PASSWORD_CHECK_REDIS, uuid);
        RBucket<String> bucket = redissonClient.getBucket(key);
        bucket.set(mobile,1, TimeUnit.HOURS);
        return uuid;
    }

    public void resetPassword(String mobile,String password,String token) {
        User user = userService.getByUsername(mobile);
        if(user == null){
            throw new ServiceException("账户不存在");
        }
        String key = String.format(RedisHttpConstant.RESET_PASSWORD_CHECK_REDIS, token);
        RBucket<String> bucket = redissonClient.getBucket(key);
        String value = bucket.get();
        if(StringUtils.isEmpty(value) || !value.equals(mobile)){
            throw new ServiceException("验证码已过期");
        }
        userService.resetPassword(user.getId(),password);
    }
}
