import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/** Event-driven mini: event log append-only, pub/sub, replay, dan proyeksi. */
public class LogKejadian {

    /** Satu event (kejadian): fakta yang sudah terjadi, tidak dapat diubah. */
    record Kejadian(String tipe, String keterangan, long nilai) {}

    /** Bus kejadian: log append-only + daftar pelanggan per tipe. */
    static class Bus {
        private final List<Kejadian> log = new ArrayList<>();
        private final Map<String, List<String>> pelangganPerTipe = new LinkedHashMap<>();

        void langganan(String tipe, String namaPelanggan) {
            pelangganPerTipe.computeIfAbsent(tipe, k -> new ArrayList<>())
                    .add(namaPelanggan);
        }

        /** Publikasi: tambahkan ke log (append-only) lalu kirim ke pelanggan tipe itu. */
        void publikasi(Kejadian k) {
            log.add(k);
            for (String p : pelangganPerTipe.getOrDefault(k.tipe(), List.of())) {
                System.out.println("    -> " + p + " menerima [" + k.tipe() + "] "
                        + k.keterangan() + " (Rp" + k.nilai() + ")");
            }
        }

        /** Replay: kirim ulang seluruh event lama bagi pelanggan yang baru bergabung. */
        void putarUlang(String tipe, String namaPelanggan) {
            for (Kejadian k : log) {
                if (k.tipe().equals(tipe)) {
                    System.out.println("    -> " + namaPelanggan + " menerima (replay) ["
                            + k.tipe() + "] " + k.keterangan() + " (Rp" + k.nilai() + ")");
                }
            }
        }

        List<Kejadian> log() {
            return log;
        }
    }

    public static void main(String[] args) {
        Bus bus = new Bus();
        bus.langganan("PesananDibuat", "svc-notifikasi");
        bus.langganan("PesananDibuat", "svc-rekomendasi");
        bus.langganan("StokMenipis", "svc-pembelian");

        System.out.println("== Publikasi: penerbit tidak mengenal pelanggan ==");
        bus.publikasi(new Kejadian("PesananDibuat", "pesanan-1001 oleh Budi", 450_000));
        bus.publikasi(new Kejadian("StokMenipis", "produk A1 sisa 2", 0));
        bus.publikasi(new Kejadian("PesananDibuat", "pesanan-1002 oleh Siti", 175_000));

        System.out.println();
        System.out.println("== Pelanggan baru mengejar dengan replay dari log ==");
        bus.langganan("PesananDibuat", "svc-audit");
        bus.putarUlang("PesananDibuat", "svc-audit");

        System.out.println();
        System.out.println("== Proyeksi: total pendapatan dihitung dari log ==");
        long total = 0;
        for (Kejadian k : bus.log()) {
            if (k.tipe().equals("PesananDibuat")) {
                total += k.nilai();
            }
        }
        System.out.println("    fold seluruh log -> total pendapatan = Rp" + total);
        System.out.println("    log utuh: " + bus.log().size()
                + " kejadian append-only, urutan penerbitan terjaga");
    }
}
