package org.dromara.web.utils;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.StrUtil;
import org.dromara.common.core.exception.user.UserException;
import org.dromara.common.redis.utils.RedisUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * token管理 1. 登陆返回token 2. 刷新token 3. 清除用户过去token 4. 校验token
 *
 * @author FrozenWatermelon
 * @date 2020/7/2
 */
@Component
public class TokenStore {

    private static final Logger logger = LoggerFactory.getLogger(TokenStore.class);

    /**
     * 以Sa-Token技术生成token，并返回token信息
     * @param userInfoInToken
     * @return
     */
    public TokenInfoBO storeAccessSaToken(UserInfoInTokenBO userInfoInToken) {
        //生成过期时间
        int timeoutSecond = getExpiresIn(userInfoInToken.getSysType());
        Duration accessTokenExpires = Duration.ofSeconds(timeoutSecond);

        String uid = this.getUid(userInfoInToken.getSysType().toString(), userInfoInToken.getUserId());
        StpUtil.login(uid, timeoutSecond);
        String token = StpUtil.getTokenValue();
        // 用户信息存入缓存 token生成
        String keyName = OauthCacheNames.USER_INFO + token;
        RedisUtils.deleteObject(keyName);
        RedisUtils.setCacheObject(keyName, userInfoInToken, accessTokenExpires);
        // 数据封装返回(token不用加密)
        TokenInfoBO tokenInfoBO = new TokenInfoBO();
        tokenInfoBO.setUserInfoInToken(userInfoInToken);
        tokenInfoBO.setExpiresIn(timeoutSecond);
        tokenInfoBO.setAccessToken(token);
        tokenInfoBO.setRefreshToken(token);
        return tokenInfoBO;
    }

    /**
     * 计算过期时间（单位:秒）
     * @param sysType
     * @return
     */
    private int getExpiresIn(int sysType) {
        // 3600秒
        int expiresIn = 3600;
        // 普通用户token过期时间
        if (Objects.equals(sysType, 0)) {
            expiresIn = expiresIn * 24 * 30;
        }
        // 系统管理员的token过期时间
        if (Objects.equals(sysType, 1)) {
            expiresIn = expiresIn * 24 * 30;
        }
        return expiresIn;
    }

    /**
     * 根据accessToken 获取用户信息
     * @param accessToken accessToken
     * @param needDecrypt 是否需要解密
     * @return 用户信息
     */
    public UserInfoInTokenBO getUserInfoByAccessToken(String accessToken, boolean needDecrypt) {
        if (StrUtil.isBlank(accessToken)) {
            throw new UserException("accessToken is blank");
        }
        String keyName = OauthCacheNames.USER_INFO + accessToken;
        Object redisCache = RedisUtils.getCacheObject(keyName);
        if (redisCache == null) {
            throw new UserException("-2","登录过期，请重新登录");
        }
        return (UserInfoInTokenBO) redisCache;
    }

    /**
     * 刷新token，并返回新的token
     * @param refreshToken
     * @return
     */
    public TokenInfoBO refreshToken(String refreshToken) {
        if (StrUtil.isBlank(refreshToken)) {
            throw new UserException("refreshToken is blank");
        }
        // 删除旧token
        UserInfoInTokenBO userInfoInTokenBO = getUserInfoByAccessToken(refreshToken, false);
        this.deleteCurrentToken(refreshToken);
        // 保存一份新的token
        return storeAccessSaToken(userInfoInTokenBO);
    }

    /**
     * 删除指定用户的全部的token
     */
    public void deleteAllToken(String sysType, Long userId) {
        // 删除用户缓存
        String uid = this.getUid(sysType, userId);
        List<String> tokens = StpUtil.getTokenValueListByLoginId(uid);
        if (!CollectionUtils.isEmpty(tokens)) {
            List<String> keyNames = new ArrayList<>();
            for (String token : tokens) {
                keyNames.add(OauthCacheNames.USER_INFO + token);
            }
            RedisUtils.deleteObject(keyNames);
        }
        // 移除token
        StpUtil.logout(userId);
    }

    /**
     * 生成token，并返回token展示信息
     * @param userInfoInToken
     * @return
     */
    public TokenInfoVO storeAndGetVo(UserInfoInTokenBO userInfoInToken) {
        if (!userInfoInToken.getEnabled()){
            // 用户已禁用，请联系客服
            throw new UserException("用户已禁用，请联系客服");
        }
        TokenInfoBO tokenInfoBO = storeAccessSaToken(userInfoInToken);
        // 数据封装返回
        TokenInfoVO tokenInfoVO = new TokenInfoVO();
        tokenInfoVO.setAccessToken(tokenInfoBO.getAccessToken());
        tokenInfoVO.setRefreshToken(tokenInfoBO.getRefreshToken());
        tokenInfoVO.setExpiresIn(tokenInfoBO.getExpiresIn());
        return tokenInfoVO;
    }

    /**
     * 删除当前登录的token
     * @param accessToken 令牌
     */
    public void deleteCurrentToken(String accessToken) {
        // 删除用户缓存
        String keyName = OauthCacheNames.USER_INFO + accessToken;
        RedisUtils.deleteObject(keyName);
        // 移除token
        StpUtil.logoutByTokenValue(accessToken);
    }

    /**
     * 生成各系统唯一uid
     * @param sysType 系统类型
     * @param userId 用户id
     * @return
     */
    private String getUid(String sysType, Long userId) {
        return sysType + ":" + userId;
    }
}
