package auctiora;

import jakarta.enterprise.context.ApplicationScoped;
import org.eclipse.microprofile.config.inject.ConfigProperty;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import io.github.bucket4j.*;

@ApplicationScoped
public class RateLimitedHttpClient {

    private final HttpClient client = HttpClient.newBuilder()
        .connectTimeout(Duration.ofSeconds(30))
        .build();

    @ConfigProperty(name = "auction.http.rate-limit.default-max-rps", defaultValue = "2")
    int defaultRps;

    @ConfigProperty(name = "auction.http.rate-limit.troostwijk-max-rps", defaultValue = "1")
    int troostwijkRps;

    @ConfigProperty(name = "auction.http.timeout-seconds", defaultValue = "30")
    int timeoutSeconds;

    private final Map<String, Bucket> buckets = new ConcurrentHashMap<>();

    private Bucket bucketForHost(String host) {
        return buckets.computeIfAbsent(host, h -> {
            int rps = host.contains("troostwijk") ? troostwijkRps : defaultRps;
            var limit = Bandwidth.simple(rps, Duration.ofSeconds(1));
            return Bucket4j.builder().addLimit(limit).build();
        });
    }

    public HttpResponse<String> sendGet(String url) throws Exception {
        var req = HttpRequest.newBuilder()
            .uri(URI.create(url))
            .timeout(Duration.ofSeconds(timeoutSeconds))
            .GET()
            .build();
        return send(req, HttpResponse.BodyHandlers.ofString());
    }

    public HttpResponse<byte[]> sendGetBytes(String url) throws Exception {
        var req = HttpRequest.newBuilder()
            .uri(URI.create(url))
            .timeout(Duration.ofSeconds(timeoutSeconds))
            .GET()
            .build();
        return send(req, HttpResponse.BodyHandlers.ofByteArray());
    }

    public <T> HttpResponse<T> send(HttpRequest req,
                                   HttpResponse.BodyHandler<T> handler) throws Exception {
        String host = req.uri().getHost();
        var bucket = bucketForHost(host);
        bucket.asBlocking().consume(1);

        var start = System.currentTimeMillis();
        var resp = client.send(req, handler);
        var duration = System.currentTimeMillis() - start;

        // (Optional) Logging
        System.out.printf("HTTP %d %s %s in %d ms%n",
            resp.statusCode(), req.method(), host, duration);

        return resp;
    }
}
