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

public class UnitKerja {
    public static void main(String[] args) {
        // Skenario sukses: tiga aksi dieksekusi berurutan dalam satu commit
        UnitKerjaDb unit = new UnitKerjaDb();
        unit.jadwalInsert("Buku Java", 85_000);
        unit.jadwalUpdate("Pena", 12_500);
        unit.jadwalHapus("Penghapus");
        unit.commit();

        // Skenario gagal: aksi kedua melempar galat, aksi pertama dibatalkan
        UnitKerjaDb unitGagal = new UnitKerjaDb();
        unitGagal.jadwalInsert("Tas", 250_000);
        unitGagal.jadwalHapus("ProdukRusak"); // sengaja gagal saat dieksekusi
        unitGagal.commit();
    }
}

// Satu aksi perubahan data, dieksekusi belakangan oleh commit()
interface AksiDb {
    String jalankan();
}

class Insert implements AksiDb {
    private final String nama;
    private final long harga;

    Insert(String nama, long harga) {
        this.nama = nama;
        this.harga = harga;
    }

    @Override
    public String jalankan() {
        return "INSERT produk (" + nama + ", Rp" + harga + ")";
    }
}

class Update implements AksiDb {
    private final String nama;
    private final long hargaBaru;

    Update(String nama, long hargaBaru) {
        this.nama = nama;
        this.hargaBaru = hargaBaru;
    }

    @Override
    public String jalankan() {
        return "UPDATE produk SET harga=" + hargaBaru + " WHERE nama=" + nama;
    }
}

class Hapus implements AksiDb {
    private final String nama;

    Hapus(String nama) {
        this.nama = nama;
    }

    @Override
    public String jalankan() {
        if (nama.startsWith("ProdukRusak")) {
            throw new IllegalStateException("produk tidak ditemukan: " + nama);
        }
        return "DELETE produk WHERE nama=" + nama;
    }
}

// Unit of Work: mengumpulkan perubahan lalu mengeksekusinya sebagai satu kesatuan
class UnitKerjaDb {
    private final List<AksiDb> aksiTerjadwal = new ArrayList<>();

    void jadwalInsert(String nama, long harga) {
        aksiTerjadwal.add(new Insert(nama, harga));
    }

    void jadwalUpdate(String nama, long hargaBaru) {
        aksiTerjadwal.add(new Update(nama, hargaBaru));
    }

    void jadwalHapus(String nama) {
        aksiTerjadwal.add(new Hapus(nama));
    }

    void commit() {
        List<String> sudahJalan = new ArrayList<>();
        System.out.println("== commit: " + aksiTerjadwal.size() + " aksi terjadwal ==");
        try {
            for (AksiDb aksi : aksiTerjadwal) {
                String hasil = aksi.jalankan();
                sudahJalan.add(hasil);
                System.out.println("  sukses: " + hasil);
            }
            aksiTerjadwal.clear();
            System.out.println("  commit selesai: " + sudahJalan.size() + " aksi menempel");
        } catch (RuntimeException e) {
            aksiTerjadwal.clear();
            System.out.println("  gagal pada aksi ke-" + (sudahJalan.size() + 1)
                    + ", batalkan " + sudahJalan.size() + " aksi sebelumnya");
            System.out.println("  penyebab: " + e.getMessage());
        }
    }
}
