package com.games.gateway.utils;

import com.alibaba.fastjson.JSON;
import com.games.gateway.domain.IpPathLimit;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.taobao.api.internal.util.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * @author mzc
 */
@Service
public class IpPathLimitCache implements InitializingBean {
    /**
     * 默认local session缓存时间 1 分钟
     */
    private static volatile Long DEFAULT_LOCAL_SESSION_CACHE_SECONDS = 1 * 60L;

    private LoadingCache<String, IpPathLimit> ipPathLimitCache;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedisKeyUtils redisKeyUtils;

    /**
     * 本地缓存使用的兜底对象
     */
    IpPathLimit defaultIpPathLimit = new IpPathLimit();

    /**
     * 获取缓存
     *
     * @param path
     * @return
     */
    public IpPathLimit get(String path) {
        // 通过get获取缓存，如果没有，则异步执行load从redis加载数据。即，多线程情况，可能会加载到旧值
        return ipPathLimitCache.get(path);
    }

    /**
     * 缓存
     *
     * @param path
     * @param ipPathLimit
     */
    public void cache(String path, IpPathLimit ipPathLimit) {
        String key = redisKeyUtils.ipPathLimitSetKey(path);
        Preconditions.checkArgument(!Strings.isNullOrEmpty(key));
        Preconditions.checkNotNull(key);
        //缓存到redis
        stringRedisTemplate.opsForValue().set(key, JSON.toJSONString(ipPathLimit));
        //本地缓存
        ipPathLimitCache.put(path, ipPathLimit);
    }

    /**
     * 失效，删除缓存
     *
     * @param path
     */
    public void invalidate(String path) {
        String key = redisKeyUtils.ipPathLimitSetKey(path);
        //删除redis
        stringRedisTemplate.delete(key);
        //删除本地缓存
        ipPathLimitCache.invalidate(key);
    }

    @Override
    public void afterPropertiesSet() {
        ipPathLimitCache = Caffeine.newBuilder()
                .expireAfterWrite(DEFAULT_LOCAL_SESSION_CACHE_SECONDS, TimeUnit.SECONDS)
                // TODO: 目前活跃用户不到1w，这里最大按50倍计算，设置50w缓存数量
                .maximumSize(5_000)
                .initialCapacity(5_000 / 10)
                .recordStats()
                .build(path -> {
                    String key = redisKeyUtils.ipPathLimitSetKey(path);
                    String cacheJson = stringRedisTemplate.opsForValue().get(key);
                    if (StringUtils.isEmpty(cacheJson)) {
                        return defaultIpPathLimit;
                    }
                    return JSON.parseObject(cacheJson, IpPathLimit.class);
                });
    }
}
