package study.gateway;

import java.time.Duration;
import java.util.List;
import java.util.UUID;
import org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter;
import org.springframework.cloud.gateway.support.ConfigurationService;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;

public final class RateLimitResources implements AutoCloseable {
    final String routeId="u32-"+UUID.randomUUID();
    final String emptyRouteId=routeId+"-empty";
    final RedisLink link;
    final LettuceConnectionFactory factory;
    final RedisRateLimiter limiter;
    final ReactiveStringRedisTemplate direct;
    RateLimitResources(ConfigurationService configuration,RedisScript<List<Long>> script,
            ReactiveStringRedisTemplate direct) throws Exception {
        this.direct=direct;
        String host=System.getProperty("spring.data.redis.host","127.0.0.1");
        int port=Integer.parseInt(System.getProperty("spring.data.redis.port","6379"));
        link=new RedisLink(host,port);
        var socket=io.lettuce.core.SocketOptions.builder().connectTimeout(Duration.ofMillis(200)).build();
        var options=io.lettuce.core.ClientOptions.builder().socketOptions(socket).build();
        var client=LettuceClientConfiguration.builder().commandTimeout(Duration.ofMillis(250))
                .shutdownTimeout(Duration.ofMillis(100)).clientOptions(options).build();
        factory=new LettuceConnectionFactory(new RedisStandaloneConfiguration("127.0.0.1",link.port()),client);
        factory.afterPropertiesSet(); factory.start();
        limiter=new RedisRateLimiter(new ReactiveStringRedisTemplate(factory),script,configuration);
        for(String id:List.of(routeId,emptyRouteId)) {
            limiter.getConfig().put(id,new RedisRateLimiter.Config().setReplenishRate(1).setBurstCapacity(60).setRequestedTokens(60));
        }
    }
    List<String> ownedKeys() {
        return List.of("request_rate_limiter.{"+routeId+".alice}.tokens",
                "request_rate_limiter.{"+routeId+".alice}.timestamp",
                "request_rate_limiter.{"+routeId+".bob}.tokens",
                "request_rate_limiter.{"+routeId+".bob}.timestamp",
                "request_rate_limiter.{"+routeId+".carol}.tokens",
                "request_rate_limiter.{"+routeId+".carol}.timestamp");
    }
    void cleanupKeys() {
        direct.delete(reactor.core.publisher.Flux.fromIterable(ownedKeys())).block(Duration.ofSeconds(3));
        for(String key:ownedKeys()) {
            if(Boolean.TRUE.equals(direct.hasKey(key).block(Duration.ofSeconds(3)))) throw new IllegalStateException("owned key remains");
        }
        System.out.println("U32 redisOwnedKeysDeleted=true ownedKeyCount=6");
    }
    public void close() throws Exception {
        // The HTTP scenario deletes and verifies its own keys in finally while
        // the Boot Redis client is running. SmartLifecycle clients can already
        // be stopped by Spring's later bean-destruction phase.
        try { factory.destroy(); }
        finally { link.close(); }
    }
}
