package banking;

import dev.banking.model.BankAccount;
import org.junit.jupiter.api.Test;

import java.time.Duration;
import java.util.concurrent.*;

import static org.junit.jupiter.api.Assertions.*;

public class BankAccountTransferDeadlockTest {

    @Test
    void deadlockFreeTransfers() {

        assertTimeoutPreemptively(Duration.ofSeconds(5), () -> {

            BankAccount a = new BankAccount(1, 1_000_000);
            BankAccount b = new BankAccount(2, 1_000_000);

            int taskCount = 50_000;

            ExecutorService executor = Executors.newFixedThreadPool(50);

            CountDownLatch startGun = new CountDownLatch(1);
            CountDownLatch finishLine = new CountDownLatch(taskCount * 2);

            for (int i = 0; i < taskCount; i++) {

                executor.submit(() -> {
                    try {
                        startGun.await();
                        a.transfer(b, 1);
                    } catch (InterruptedException ignored) {
                        Thread.currentThread().interrupt();
                    } finally {
                        finishLine.countDown();
                    }
                });

                executor.submit(() -> {
                    try {
                        startGun.await();
                        b.transfer(a, 1);
                    } catch (InterruptedException ignored) {
                        Thread.currentThread().interrupt();
                    } finally {
                        finishLine.countDown();
                    }
                });
            }

            startGun.countDown();

            boolean completed;

            try {
                completed = finishLine.await(5, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException(e);
            }

            executor.shutdown();

            assertTrue(completed, "Deadlock detected: tasks did not complete in time");
        });
    }
}