package com.hys.app.framework.apptoken.impl;

import com.hys.app.framework.apptoken.AccessTokenCache;
import com.hys.app.framework.apptoken.model.AccessToken;
import com.hys.app.framework.apptoken.AccessTokenIdentifier;
import com.hys.app.framework.cache.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.hys.app.framework.util.DateUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

/**
 * access token二级缓存的实现
 * 一级缓存使用本jvm的static变量
 * 二级缓存使用redis缓存
 * @author kingapex
 * @version 1.0
 * @description TODO
 * @data 2021/8/23 17:49
 **/
@Service
public class AccessTokenCacheDefaultImpl implements AccessTokenCache {

    @Autowired
    private Cache redisCache;

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private static Map<String, AccessToken> localCache;
    static {
        localCache = new HashMap<>();
    }

    /**
     * 先由本地缓存中读取token
     * 如果不存在或已经失效则由redis中读取，如果有效则存入本地缓存
     * 最终判断如果token有效则返回，否则返回null
     * @param accessTokenIdentifier token的唯一标识
     * @return
     */
    @Override
    public AccessToken get(AccessTokenIdentifier accessTokenIdentifier){
        String cacheKey = getCacheKey(accessTokenIdentifier);
        Long now = DateUtil.getDateline();

        //先从本地缓存获取token
        AccessToken accessToken = localCache.get(cacheKey);

        logger.debug("由本地缓存获取token "+ accessToken);


        //不存在或已经失效，则由redis缓存中获取
        if (accessToken==null || now > accessToken.getExpires()) {
            accessToken = (AccessToken) redisCache.get(cacheKey);
            logger.debug("由redis缓存获取token "+ accessToken);
            //redis缓存中的token不为null 并且有效，则压入本地缓存
            if (accessToken!=null && now < accessToken.getExpires()) {
                localCache.put(cacheKey, accessToken);
            }
        }

        //如果token有效，则返回，否则返回null
        if (accessToken!=null &&  now < accessToken.getExpires()) {
            return accessToken;
        }
        return null;
    }

    /**
     * 将token同时放入redis缓存和本地缓存
     * @param accessTokenIdentifier token的唯一标识
     * @param accessToken 要缓存的token
     */
    @Override
    public void put(AccessTokenIdentifier accessTokenIdentifier, AccessToken accessToken) {
        String cacheKey = getCacheKey(accessTokenIdentifier);
        localCache.put(cacheKey, accessToken);
        redisCache.put(cacheKey, accessToken,accessToken.getExpires().intValue());
    }

    /**
     * 获取缓存的key
     * 本地缓存是一个map，这个key是map中的key
     * reids缓存使用这个Key作为缓存key
     * @param accessTokenIdentifier
     * @return
     */
    private String getCacheKey(AccessTokenIdentifier accessTokenIdentifier) {

        return "{access_token}_" + accessTokenIdentifier.buildId();
    }

}
