// Author: Sandeep Belgavi. Synthetic admission check; no tool or network calls.
import java.util.ArrayList;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class FanoutDemo {
    public static void main(String[] args) throws Exception {
        var permits = new Semaphore(4);
        var release = new CountDownLatch(1);
        var entered = new CountDownLatch(4);
        var active = new AtomicInteger();
        var peak = new AtomicInteger();
        var handles = new ArrayList<Future<?>>();
        int rejected = 0;
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            try {
                for (int i = 0; i < 12; i++) {
                    if (!permits.tryAcquire()) { rejected++; continue; }
                    try {
                        handles.add(executor.submit(() -> {
                            int n = active.incrementAndGet();
                            peak.accumulateAndGet(n, Math::max);
                            entered.countDown();
                            try { release.await(); }
                            catch (InterruptedException e) { Thread.currentThread().interrupt(); }
                            finally { active.decrementAndGet(); permits.release(); }
                        }));
                    } catch (RejectedExecutionException e) {
                        permits.release(); // Submission failed before a worker owned it.
                        throw e;
                    }
                }
                if (!entered.await(5, TimeUnit.SECONDS)) throw new AssertionError("Workers stalled");
            } finally { release.countDown(); }
            for (var handle : handles) handle.get(5, TimeUnit.SECONDS);
        }
        if (peak.get() != 4 || rejected != 8 || permits.availablePermits() != 4)
            throw new AssertionError("Admission invariant failed");
        System.out.println("admitted=4 rejected=8 peak=4 permits_restored=4");
    }
}
