package com.easyagents.flow.core.test;

import com.easyagents.flow.core.chain.runtime.RetryableTriggerException;
import com.easyagents.flow.core.util.IoBulkhead;
import org.junit.Assert;
import org.junit.Test;

import java.time.Duration;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * {@link IoBulkhead} 有界并发与可观测性回归测试。
 */
public class IoBulkheadTest {

    /**
     * 验证单目标饱和时快速拒绝，同时其他目标仍可使用剩余全局容量。
     *
     * @throws Exception 并发测试执行失败时抛出
     */
    @Test
    public void shouldIsolateSaturatedTargetWithoutExhaustingGlobalCapacity() throws Exception {
        IoBulkhead bulkhead = new IoBulkhead(2, 1, Duration.ofMillis(50), 4);
        ExecutorService executor = Executors.newSingleThreadExecutor();
        try (IoBulkhead.Permit first = bulkhead.acquire("http:first");
             IoBulkhead.Permit second = bulkhead.acquire("http:second")) {
            Future<?> rejected = executor.submit(() -> bulkhead.acquire("http:first"));
            try {
                rejected.get();
                Assert.fail("same target should be rejected");
            } catch (ExecutionException exception) {
                Assert.assertTrue(exception.getCause() instanceof RetryableTriggerException);
            }

            IoBulkhead.Snapshot snapshot = bulkhead.snapshot();
            Assert.assertEquals(2L, snapshot.acquiredCount());
            Assert.assertEquals(1L, snapshot.rejectedCount());
            Assert.assertEquals(2L, snapshot.inFlightCount());
            Assert.assertEquals(0, snapshot.availableGlobalPermits());
            Assert.assertEquals(2, snapshot.trackedTargetCount());
        } finally {
            executor.shutdownNow();
        }

        Assert.assertEquals(0L, bulkhead.snapshot().inFlightCount());
        Assert.assertEquals(2, bulkhead.snapshot().availableGlobalPermits());
    }

    /**
     * 验证 URL 目标键只保留协议无关的主机和显式端口。
     */
    @Test
    public void shouldResolveStableHttpTarget() {
        Assert.assertEquals(
                "http:example.com:8443",
                IoBulkhead.targetForUrl("https://EXAMPLE.com:8443/path?q=1"));
        Assert.assertEquals("http:unknown", IoBulkhead.targetForUrl("not a url"));
    }

    /**
     * 验证高基数目标不会突破目标信号量注册上限。
     */
    @Test
    public void shouldKeepTrackedTargetRegistryBounded() {
        IoBulkhead bulkhead = new IoBulkhead(4, 2, Duration.ofMillis(50), 2);

        try (IoBulkhead.Permit ignored = bulkhead.acquire("target:first")) {
            // 仅触发目标注册。
        }
        try (IoBulkhead.Permit ignored = bulkhead.acquire("target:second")) {
            // 仅触发目标注册。
        }
        try (IoBulkhead.Permit ignored = bulkhead.acquire("target:overflow")) {
            // 超额目标统一使用有界的 overflow 信号量。
        }

        Assert.assertEquals(2, bulkhead.snapshot().trackedTargetCount());
    }
}
