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

/** Distributed tracing mini: trace context via ThreadLocal dan jejak span lintas service. */
public class JejakTerdistribusi {

    /** Konteks yang mengalir bersama eksekusi: id trace, id span, id span ortu. */
    record KonteksTrace(String traceId, String spanId, String spanIdOrtu) {}

    /** ThreadLocal membawa konteks sehingga seluruh method ikut tanpa parameter tambahan. */
    static final ThreadLocal<KonteksTrace> KONTEKS = new ThreadLocal<>();

    /** Satu span tercatat: potongan pekerjaan dalam satu trace. */
    record Span(String traceId, String spanId, String spanIdOrtu, String layanan, String nama) {}

    static final List<Span> SPAN = new ArrayList<>();

    /** Log terstruktur: setiap baris otomatis membawa trace id dan span id. */
    static void log(String pesan) {
        KonteksTrace k = KONTEKS.get();
        System.out.println("  [trace=" + k.traceId() + " span=" + k.spanId() + "] " + pesan);
    }

    /** Buka satu span: id baru, catat, jalankan isi, lalu pulihkan konteks pemanggil. */
    static void span(String layanan, String nama, Runnable isi) {
        KonteksTrace ortu = KONTEKS.get();
        String traceId = (ortu == null) ? "t-4f21" : ortu.traceId();
        String spanBaru = "s-" + (SPAN.size() + 1);
        String ortuId = (ortu == null) ? null : ortu.spanId();

        KONTEKS.set(new KonteksTrace(traceId, spanBaru, ortuId));
        SPAN.add(new Span(traceId, spanBaru, ortuId, layanan, nama));
        log(layanan + " mulai span \"" + nama + "\"");

        isi.run();

        log(layanan + " selesai span \"" + nama + "\"");
        KONTEKS.set(ortu); // kembalikan konteks pemanggil
    }

    /** Panggilan lintas service: pada sistem nyata konteks menumpang header HTTP. */
    static void panggilLayanan(String layanan, String namaSpan, Runnable isi) {
        span(layanan, namaSpan, isi);
    }

    public static void main(String[] args) {
        System.out.println("== Log terstruktur selama satu permintaan berjalan ==");
        span("svc-order", "POST /api/pesanan", () -> {
            span("svc-order", "validasi pesanan", () -> log("isi keranjang diperiksa"));
            panggilLayanan("svc-pembayaran", "POST /tagih", () -> {
                span("svc-pembayaran", "tagih kartu", () -> log("permintaan ke gateway"));
            });
            panggilLayanan("svc-notifikasi", "kirim email", () -> log("email konfirmasi"));
        });

        System.out.println();
        System.out.println("== Jejak lengkap: seluruh span milik satu traceId ==");
        System.out.println("traceId = " + SPAN.get(0).traceId() + ", jumlah span = " + SPAN.size());
        for (Span s : SPAN) {
            String ortu = (s.spanIdOrtu() == null) ? "-" : s.spanIdOrtu();
            System.out.println("  " + s.spanId() + " (ortu " + String.format("%-4s", ortu) + ") "
                    + String.format("%-16s", s.layanan()) + s.nama());
        }
    }
}
