package com.easyagents.flow.core.test;

import com.easyagents.flow.core.util.IoBulkhead;
import com.easyagents.flow.core.util.OkHttpClientUtil;
import com.sun.net.httpserver.HttpServer;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.junit.Assert;
import org.junit.Test;

import java.net.InetSocketAddress;
import java.time.Duration;

/**
 * {@link OkHttpClientUtil} 共享连接池回归测试。
 */
public class OkHttpClientUtilTest {

    /**
     * 验证默认客户端跨节点调用复用同一实例。
     */
    @Test
    public void shouldReuseDefaultClientInstance() {
        OkHttpClient first = OkHttpClientUtil.buildDefaultClient();
        OkHttpClient second = OkHttpClientUtil.buildDefaultClient();

        Assert.assertSame(first, second);
        Assert.assertSame(first.connectionPool(), second.connectionPool());
        Assert.assertSame(first.dispatcher(), second.dispatcher());
        Assert.assertEquals(
                1L,
                first.interceptors().stream()
                        .filter(interceptor -> interceptor.getClass().getSimpleName()
                                .equals("IoBulkheadInterceptor"))
                        .count());
    }

    /**
     * 验证非幂等请求客户端关闭底层隐式重试且继续复用连接资源。
     */
    @Test
    public void shouldReuseNoRetryClientAndConnectionPool() {
        OkHttpClient defaultClient = OkHttpClientUtil.buildDefaultClient();
        OkHttpClient first = OkHttpClientUtil.buildNoRetryClient();
        OkHttpClient second = OkHttpClientUtil.buildNoRetryClient();

        Assert.assertSame(first, second);
        Assert.assertFalse(first.retryOnConnectionFailure());
        Assert.assertSame(defaultClient.connectionPool(), first.connectionPool());
        Assert.assertSame(defaultClient.dispatcher(), first.dispatcher());
    }

    /**
     * 验证单目标并发为一时一次 HTTP 请求只申请一次许可且不会自锁。
     *
     * @throws Exception 启动本地 HTTP 服务或请求失败时抛出
     */
    @Test
    public void shouldAcquireOnePermitPerHttpRequest() throws Exception {
        IoBulkhead.Settings restricted =
                new IoBulkhead.Settings(1, 1, Duration.ofMillis(200), 16);
        IoBulkhead.configure(
                restricted,
                new IoBulkhead.Settings(32, 8, Duration.ofSeconds(1), 512),
                new IoBulkhead.Settings(24, 12, Duration.ofSeconds(2), 256),
                new IoBulkhead.Settings(8, 4, Duration.ofSeconds(2), 128),
                new IoBulkhead.Settings(8, 4, Duration.ofSeconds(2), 1_024));
        HttpServer server = HttpServer.create(
                new InetSocketAddress("127.0.0.1", 0), 0);
        server.createContext("/", exchange -> {
            byte[] body = "ok".getBytes(java.nio.charset.StandardCharsets.UTF_8);
            exchange.sendResponseHeaders(200, body.length);
            exchange.getResponseBody().write(body);
            exchange.close();
        });
        server.start();
        try {
            long acquiredBefore = IoBulkhead.shared()
                    .snapshot()
                    .acquiredCount();
            Request request = new Request.Builder()
                    .url("http://127.0.0.1:"
                            + server.getAddress().getPort()
                            + "/")
                    .build();
            try (Response response = OkHttpClientUtil
                    .buildNoRetryClient()
                    .newCall(request)
                    .execute()) {
                Assert.assertEquals(200, response.code());
            }
            Assert.assertEquals(
                    acquiredBefore + 1,
                    IoBulkhead.shared().snapshot().acquiredCount());
            Assert.assertEquals(
                    0L,
                    IoBulkhead.shared().snapshot().inFlightCount());
        } finally {
            server.stop(0);
            restoreDefaultBulkheads();
        }
    }

    /**
     * 恢复测试进程的宽松默认隔离配置，避免污染其他测试。
     */
    private void restoreDefaultBulkheads() {
        IoBulkhead.configure(
                new IoBulkhead.Settings(64, 16, Duration.ofSeconds(1), 1_024),
                new IoBulkhead.Settings(32, 8, Duration.ofSeconds(1), 512),
                new IoBulkhead.Settings(24, 12, Duration.ofSeconds(2), 256),
                new IoBulkhead.Settings(8, 4, Duration.ofSeconds(2), 128),
                new IoBulkhead.Settings(8, 4, Duration.ofSeconds(2), 1_024));
    }
}
