import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;

public class ObservasiMini {

    // ==== Pilar 1: log terstruktur dengan konteks jejak (padanan MDC) ====
    record KonteksJejak(String traceId, String spanId) {

        KonteksJejak gantiSpan(String spanBaru) {
            return new KonteksJejak(traceId, spanBaru);
        }
    }

    static void log(String tingkat, KonteksJejak ctx, String pesan,
            Object... kv) {
        StringBuilder baris = new StringBuilder("tingkat=" + tingkat)
                .append(" trace_id=").append(ctx.traceId())
                .append(" span_id=").append(ctx.spanId())
                .append(" pesan=\"").append(pesan).append('"');
        for (int i = 0; i + 1 < kv.length; i += 2) {
            baris.append(' ').append(kv[i]).append('=').append(kv[i + 1]);
        }
        System.out.println(baris);
    }

    // ==== Pilar 2: registri metrik mini ala Micrometer ====
    static class RegistriMetrik {

        private final Map<String, Double> counter = new TreeMap<>();
        private final Map<String, Long> gauge = new TreeMap<>();
        private final Map<String, List<Double>> timer = new TreeMap<>();

        void counterNaik(String nama) {
            counter.merge(nama, 1.0, Double::sum);
        }

        void gauge(String nama, long nilai) {
            gauge.put(nama, nilai);
        }

        void timerCatat(String nama, double detik) {
            timer.computeIfAbsent(nama, k -> new ArrayList<>()).add(detik);
        }

        // Eksposur ala Prometheus: nama terurut, satu baris per meter.
        String ekspos() {
            StringBuilder teks = new StringBuilder();
            for (Map.Entry<String, Double> e : counter.entrySet()) {
                teks.append(e.getKey()).append(' ')
                        .append(String.format(Locale.ROOT, "%.1f", e.getValue()))
                        .append('\n');
            }
            for (Map.Entry<String, Long> e : gauge.entrySet()) {
                teks.append(e.getKey()).append(' ').append(e.getValue())
                        .append('\n');
            }
            for (Map.Entry<String, List<Double>> e : timer.entrySet()) {
                double maks = e.getValue().stream().mapToDouble(Double::doubleValue).max().orElse(0);
                teks.append(e.getKey()).append("_count ").append(e.getValue().size())
                        .append('\n')
                        .append(e.getKey()).append("_max ")
                        .append(String.format(Locale.ROOT, "%.3f", maks))
                        .append('\n');
            }
            return teks.toString();
        }
    }

    // ==== Pilar 3: pohon span (padanan trace terdistribusi) ====
    record Span(String id, String nama, double durasiDetik, List<Span> anak) {

        Span(String id, String nama, double durasiDetik) {
            this(id, nama, durasiDetik, new ArrayList<>());
        }

        void tambahAnak(Span s) {
            anak.add(s);
        }
    }

    static void cetakSpan(String prefiks, String label, Span s) {
        System.out.printf("%s%s%s %s (%.3fs)%n", prefiks, label, s.id(), s.nama(),
                s.durasiDetik());
        for (int i = 0; i < s.anak().size(); i++) {
            boolean terakhir = i == s.anak().size() - 1;
            cetakSpan(prefiks + (terakhir ? "    " : "  |  "),
                    terakhir ? "`-- " : "|-- ", s.anak().get(i));
        }
    }

    public static void main(String[] args) {
        RegistriMetrik metrik = new RegistriMetrik();

        // ---- Satu request mengalir: log + trace + metrik tercatat bersama ----
        System.out.println("== Pilar 1: log terstruktur (tercetak selama alur) ==");
        String traceId = "4bf92f3577b34da6a3ce929d0e0e4736";
        KonteksJejak ctx = new KonteksJejak(traceId, "00f067aa0ba902b7");
        log("INFO", ctx, "request diterima", "method", "POST", "path", "/api/pesanan");

        KonteksJejak ctxLayanan = ctx.gantiSpan("00f067aa0ba902b8");
        log("INFO", ctxLayanan, "menyimpan pesanan", "pesananId", "P-101");

        KonteksJejak ctxDb = ctxLayanan.gantiSpan("00f067aa0ba902b9");
        log("INFO", ctxDb, "insert ke tabel", "tabel", "pesanan", "baris", 1);

        log("INFO", ctx, "request selesai", "status", 201);
        metrik.counterNaik("pesanan_dibuat_total");
        metrik.timerCatat("pesanan_durasi_seconds", 0.012);

        // Dua request berikutnya hanya menambah metrik (log dilewati agar ringkas).
        metrik.counterNaik("pesanan_dibuat_total");
        metrik.timerCatat("pesanan_durasi_seconds", 0.007);
        metrik.counterNaik("pesanan_dibuat_total");
        metrik.timerCatat("pesanan_durasi_seconds", 0.025);
        metrik.gauge("antrian_kirim_panjang", 7);

        System.out.println();
        System.out.println("== Pilar 2: metrik (eksposur ala Prometheus) ==");
        System.out.print(metrik.ekspos());

        System.out.println("== Pilar 3: pohon trace ==");
        System.out.println("TRACE " + traceId);
        Span akar = new Span("00f067aa0ba902b7", "POST /api/pesanan", 0.012);
        Span layanan = new Span("00f067aa0ba902b8", "LayananPesanan.simpan", 0.012);
        Span db = new Span("00f067aa0ba902b9", "db.insert", 0.007);
        layanan.tambahAnak(db);
        akar.tambahAnak(layanan);
        cetakSpan("", "", akar);
    }
}
