package main

import (
	"fmt"
	"io"
	"maps"
	"math"
	"net/http"
	"net/http/httptest"
	"slices"
	"strconv"
)

// counterProm memodelkan satu metrik counter dengan label "status".
type counterProm struct {
	nama    string
	bantuan string
	nilai   map[string]float64
}

// histogramProm memodelkan histogram: observasi mentah + batas bucket.
type histogramProm struct {
	nama      string
	bantuan   string
	batas     []float64
	observasi []float64
}

// tulisCounter merender satu keluarga counter ke format teks Prometheus.
func tulisCounter(w io.Writer, c counterProm) {
	fmt.Fprintf(w, "# HELP %s %s\n", c.nama, c.bantuan)
	fmt.Fprintf(w, "# TYPE %s counter\n", c.nama)
	for _, label := range slices.Sorted(maps.Keys(c.nilai)) {
		fmt.Fprintf(w, "%s{status=%q} %g\n", c.nama, label, c.nilai[label])
	}
}

// tulisHistogram merender histogram: _bucket kumulatif, _sum, dan _count.
func tulisHistogram(w io.Writer, h histogramProm) {
	fmt.Fprintf(w, "# HELP %s %s\n", h.nama, h.bantuan)
	fmt.Fprintf(w, "# TYPE %s histogram\n", h.nama)
	for _, batas := range append(slices.Clone(h.batas), math.Inf(1)) {
		n := 0
		for _, v := range h.observasi {
			if v <= batas {
				n++
			}
		}
		fmt.Fprintf(w, "%s_bucket{le=%q} %d\n", h.nama, formatBatas(batas), n)
	}
	jumlah := 0.0
	for _, v := range h.observasi {
		jumlah += v
	}
	fmt.Fprintf(w, "%s_sum %s\n", h.nama, formatAngka(jumlah))
	fmt.Fprintf(w, "%s_count %d\n", h.nama, len(h.observasi))
}

// formatBatas menuliskan batas bucket: "+Inf" atau angka biasa.
func formatBatas(b float64) string {
	if math.IsInf(b, 1) {
		return "+Inf"
	}
	return strconv.FormatFloat(b, 'g', -1, 64)
}

// formatAngka membulatkan ke dua desimal agar bebas derau floating point.
func formatAngka(v float64) string {
	return strconv.FormatFloat(math.Round(v*100)/100, 'g', -1, 64)
}

func main() {
	pesanan := counterProm{
		nama:    "pesanan_diproses_total",
		bantuan: "Jumlah pesanan yang selesai diproses.",
		nilai:   map[string]float64{"sukses": 8, "gagal": 2},
	}
	durasi := histogramProm{
		nama:      "durasi_request_seconds",
		bantuan:   "Distribusi durasi penanganan request.",
		batas:     []float64{0.1, 0.5, 1},
		observasi: []float64{0.05, 0.12, 0.18, 0.22, 0.31, 0.44, 0.58, 0.66, 0.74, 0.9},
	}

	// Endpoint /metrics diekspos lewat mux dengan pola method + path.
	mux := http.NewServeMux()
	mux.HandleFunc("GET /metrics", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/plain; version=0.0.4; charset=utf-8")
		tulisCounter(w, pesanan)
		tulisHistogram(w, durasi)
	})
	server := httptest.NewServer(mux)
	defer server.Close()

	// Scrape: pengambilan dilakukan klien, bukan aplikasi yang mendorong.
	resp, err := http.Get(server.URL + "/metrics")
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	isi, _ := io.ReadAll(resp.Body)

	fmt.Println("--- scrape GET /metrics ---")
	fmt.Println("status:", resp.StatusCode)
	fmt.Println("content-type:", resp.Header.Get("Content-Type"))
	fmt.Println("--- isi respons ---")
	fmt.Print(string(isi))
}
