import java.util.ArrayList;
import java.util.List;

public class BankOop {
    public static void main(String[] args) {
        Customer asha = new Customer("Asha");
        asha.open(new SavingsAccount("SB-101", 100_000, 4.0));
        asha.open(new CurrentAccount("CA-201", 20_000));

        System.out.println("-- Encapsulation: state changes only through rules --");
        Account sb = asha.accounts().get(0);
        sb.deposit(5_000);
        System.out.println(sb.id() + " balance = " + sb.balance());
        try {
            sb.deposit(-1);
        } catch (IllegalArgumentException e) {
            System.out.println("rejected: " + e.getMessage());
        }
        try {
            sb.withdraw(1_000_000);
        } catch (IllegalStateException e) {
            System.out.println("rejected: " + e.getMessage());
        }

        System.out.println("-- Polymorphism: one call, different behaviour --");
        for (Account a : asha.accounts()) {
            System.out.printf("%s (%s) month-end interest = %.2f%n",
                    a.id(), a.getClass().getSimpleName(), a.monthEndInterest());
        }

        System.out.println("-- Overdraft limit is inherited behaviour, overridden --");
        Account ca = asha.accounts().get(1);
        ca.withdraw(24_000);
        System.out.println(ca.id() + " balance after 24000 withdrawal = " + ca.balance());

        System.out.println("-- Abstraction: callers depend on Statement only --");
        for (Account a : asha.accounts()) {
            Statement s = a;
            System.out.println(s.summary());
        }

        System.out.println("-- IS-A vs HAS-A --");
        System.out.println("SavingsAccount IS-A Account: " + (sb instanceof Account));
        System.out.println("Customer HAS-A list of accounts: " + asha.accounts().size());
        System.out.println("Customer IS-A Account: " + ((Object) asha instanceof Account));
    }
}

interface Statement {
    String summary();
}

abstract class Account implements Statement {
    private final String id;
    private double balance;

    protected Account(String id, double opening) {
        if (opening < 0) throw new IllegalArgumentException("opening balance must be >= 0");
        this.id = id;
        this.balance = opening;
    }

    public void deposit(double amount) {
        if (amount <= 0) throw new IllegalArgumentException("deposit must be positive");
        balance += amount;
    }

    public void withdraw(double amount) {
        if (amount > balance + overdraftLimit()) throw new IllegalStateException("insufficient funds");
        balance -= amount;
    }

    public double balance() { return balance; }
    public String id() { return id; }

    protected double overdraftLimit() { return 0; }

    public abstract double monthEndInterest();

    @Override
    public String summary() {
        return id + " | balance " + balance;
    }
}

class SavingsAccount extends Account {
    private final double annualRatePercent;

    SavingsAccount(String id, double opening, double annualRatePercent) {
        super(id, opening);
        this.annualRatePercent = annualRatePercent;
    }

    @Override
    public double monthEndInterest() {
        return balance() * annualRatePercent / 100 / 12;
    }
}

class CurrentAccount extends Account {
    CurrentAccount(String id, double opening) { super(id, opening); }

    @Override
    protected double overdraftLimit() { return 5_000; }

    @Override
    public double monthEndInterest() { return 0; }

    @Override
    public String summary() {
        return super.summary() + " | overdraft limit " + overdraftLimit();
    }
}

class Customer {
    private final String name;
    private final List<Account> accounts = new ArrayList<>();

    Customer(String name) { this.name = name; }

    void open(Account a) { accounts.add(a); }

    List<Account> accounts() { return List.copyOf(accounts); }
}
