package com.games.gateway.utils;

import com.games.gateway.enums.LimiterTypeEnum;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class LimitCache implements InitializingBean {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    private LoadingCache<String, Integer> limitCache;

    @Autowired
    private RedisKeyUtils redisKeyUtils;

    public String getRedisKey(LimiterTypeEnum type, String key) {
        String redisKey = "";
        if (LimiterTypeEnum.TOKEN.equals(type)) {
            redisKey = redisKeyUtils.tokenLimiterKey(key);
        } else if (LimiterTypeEnum.IP.equals(type)) {
            redisKey = redisKeyUtils.ipLimiterKey(key);
        } else if (LimiterTypeEnum.PATH.equals(type)) {
            redisKey = redisKeyUtils.pathLimiterKey(key);
        }
        return redisKey;
    }

    public Integer get(LimiterTypeEnum type, String key) {
        String redisKey = getRedisKey(type, key);
        return limitCache.get(redisKey);
    }

    public void cache(LimiterTypeEnum type, String key, Integer num) {
        String redisKey = getRedisKey(type, key);
        redisTemplate.opsForValue().set(redisKey, String.valueOf(num));
        limitCache.put(redisKey, Integer.valueOf(num));
    }

    /**
     * 初始化 JVM Cache
     */
    @Override
    public void afterPropertiesSet() {
        limitCache = Caffeine.newBuilder()
                .expireAfterWrite(30, TimeUnit.SECONDS)
                .maximumSize(500_000)
                .initialCapacity(500_000 / 50)
                .build(key -> {
                    if (!redisTemplate.hasKey(key)) {
                        return 0;
                    }
                    String numStr = redisTemplate.opsForValue().get(key);
                    return Integer.valueOf(numStr);
                });
    }
}
