package dev.banking.model;

import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class BankAccount {



    private final int accountId;
    private long balance;
    private Lock accountLock = new ReentrantLock();
    private Condition accountCon = accountLock.newCondition();
    /*
     * Students may introduce additional fields
     * such as:
     * - Lock / ReentrantLock
     * - ReadWriteLock
     * - Object monitor
     * - etc.
     */

    public BankAccount(int accountId, long initialBalance) {
        this.accountId = accountId;
        this.balance = initialBalance;
    }

    public int getAccountId() {
        return accountId;
    }

    public long getBalance() {
        accountLock.lock();
        try{
            return this.balance;
        }finally {
            accountLock.unlock();
        }
    }


    public void deposit(long amount) {
        accountLock.lock();
        try{
            balance += amount;
            accountCon.signalAll();
        }finally {
            accountLock.unlock();
        }
    }

    public void withdraw(long amount) {
        accountLock.lock();
        try{
            while (balance < amount){
                accountCon.await();
            }
            balance -= amount;
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            accountLock.unlock();
        }

    }


    public void transfer(BankAccount target, long amount) {

        if(this.getAccountId() == target.getAccountId()){
            return;
        }

        BankAccount first = this.getAccountId() < target.getAccountId() ? this : target;
        BankAccount second = this.getAccountId() < target.getAccountId() ? target : this;


        first.accountLock.lock();

        try {

            second.accountLock.lock();
            try {

                while (this.balance < amount) {
                    this.accountCon.await();
                }
                this.balance -= amount;
                target.balance += amount;

                this.accountCon.signalAll();

            }catch (InterruptedException e){
                throw new RuntimeException();
            }finally {
             second.accountLock.unlock();
            }



        }finally {
            first.accountLock.unlock();
        }

    }
}