import java.util.HashMap;
import java.util.Map;

public class TransaksiMini {

    static class SaldoTidakCukupException extends RuntimeException {
        SaldoTidakCukupException(String pesan) { super(pesan); }
    }

    // "Tabel" sederhana: akun -> saldo.
    static class Dompet {
        final Map<String, Long> saldo = new HashMap<>();

        void ubah(String akun, long delta) {
            saldo.merge(akun, delta, Long::sum);
        }

        long cek(String akun) {
            return saldo.getOrDefault(akun, 0L);
        }

        Dompet snapshot() {
            Dompet salinan = new Dompet();
            salinan.saldo.putAll(saldo);
            return salinan;
        }

        void pulihkan(Dompet salinan) {
            saldo.clear();
            saldo.putAll(salinan.saldo);
        }
    }

    // Wrapper @Transactional mini: snapshot awal, commit bila sukses,
    // rollback penuh bila RuntimeException lewat.
    static void transaksi(String label, Dompet dompet, Runnable aksi) {
        System.out.println("TRANSAKSI [" + label + "] mulai");
        Dompet salinan = dompet.snapshot();
        try {
            aksi.run();
            System.out.println("  -> COMMIT");
        } catch (RuntimeException e) {
            dompet.pulihkan(salinan);
            System.out.println("  -> ROLLBACK: " + e.getMessage());
        }
    }

    public static void main(String[] args) {
        Dompet dompet = new Dompet();
        dompet.ubah("budi", 100_000);
        dompet.ubah("siti", 50_000);
        System.out.println("saldo awal  : budi=" + dompet.cek("budi")
                + " siti=" + dompet.cek("siti"));
        System.out.println();

        transaksi("transfer sukses", dompet, () -> {
            dompet.ubah("budi", -30_000);
            dompet.ubah("siti", 30_000);
        });
        System.out.println("  saldo     : budi=" + dompet.cek("budi")
                + " siti=" + dompet.cek("siti"));
        System.out.println();

        transaksi("transfer gagal", dompet, () -> {
            dompet.ubah("budi", -500_000);
            if (dompet.cek("budi") < 0) {
                throw new SaldoTidakCukupException("saldo budi tidak cukup");
            }
            dompet.ubah("siti", 500_000);
        });
        System.out.println("  saldo     : budi=" + dompet.cek("budi")
                + " siti=" + dompet.cek("siti"));
        System.out.println();

        transaksi("transfer kecil setelah rollback", dompet, () -> {
            dompet.ubah("siti", -10_000);
            dompet.ubah("budi", 10_000);
        });
        System.out.println("  saldo     : budi=" + dompet.cek("budi")
                + " siti=" + dompet.cek("siti"));
    }
}
