// Async.java — CompletableFuture: run work in the background and combine results.
import java.util.List;
import java.util.concurrent.*;

public class Async {

    // Simulate slow remote calls (an API, a database)
    static String fetchCustomer(String id) { sleep(300); return "Customer " + id; }
    static double fetchCreditLimit(String id) { sleep(400); return 5000.0; }
    static double fetchOpenBalance(String id) { sleep(200); return 1200.0; }

    static void sleep(long ms) {
        try { Thread.sleep(ms); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
    }

    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(4);
        long start = System.currentTimeMillis();

        // Start three calls at once
        CompletableFuture<String> customer = CompletableFuture.supplyAsync(() -> fetchCustomer("C1"), pool);
        CompletableFuture<Double> limit = CompletableFuture.supplyAsync(() -> fetchCreditLimit("C1"), pool);
        CompletableFuture<Double> balance = CompletableFuture.supplyAsync(() -> fetchOpenBalance("C1"), pool);

        // Combine when two are done
        CompletableFuture<Double> available = limit.thenCombine(balance, (l, b) -> l - b);

        // Transform and consume
        String summary = customer
                .thenCombine(available, (c, a) -> c + " can spend " + a)
                .thenApply(String::toUpperCase)
                .join();                                     // wait for the result
        System.out.println(summary);
        long took = System.currentTimeMillis() - start;
        System.out.println("Took ~" + took + " ms (one after another it would be ~900 ms)");

        // Handle failures without try/catch everywhere
        CompletableFuture<String> risky = CompletableFuture
                .supplyAsync(() -> { if (true) throw new IllegalStateException("API down"); return "ok"; }, pool)
                .exceptionally(ex -> "fallback (" + ex.getCause().getMessage() + ")");
        System.out.println(risky.join());

        // Wait for many
        List<CompletableFuture<String>> calls = List.of("C1", "C2", "C3").stream()
                .map(id -> CompletableFuture.supplyAsync(() -> fetchCustomer(id), pool))
                .toList();
        CompletableFuture.allOf(calls.toArray(new CompletableFuture[0])).join();
        calls.forEach(f -> System.out.println(f.join()));

        // Timeouts (Java 9+)
        String slow = CompletableFuture.supplyAsync(() -> { sleep(2000); return "late"; }, pool)
                .completeOnTimeout("timed out", 500, TimeUnit.MILLISECONDS)
                .join();
        System.out.println(slow);

        pool.shutdownNow();
    }
}
