package main

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"sync"
)

// StatusDB menirukan koneksi database: bisa hidup atau mati.
type StatusDB struct {
	mu   sync.Mutex
	siap bool
}

// Ping melaporkan apakah database saat ini merespons.
func (d *StatusDB) Ping() bool {
	d.mu.Lock()
	defer d.mu.Unlock()
	return d.siap
}

// SetSiap mengubah kondisi database untuk keperluan skenario demo.
func (d *StatusDB) SetSiap(v bool) {
	d.mu.Lock()
	defer d.mu.Unlock()
	d.siap = v
}

// responsSiap adalah bentuk respons JSON saat readiness gagal.
type responsSiap struct {
	Status   string `json:"status"`
	Penyebab string `json:"penyebab"`
}

// Aplikasi memegang dependency yang diperiksa oleh handler readiness.
type Aplikasi struct {
	db *StatusDB
}

// handlerHealthz adalah liveness probe: hanya membuktikan proses hidup.
// Tidak ada pemeriksaan dependency berat di sini — sengaja.
func (a *Aplikasi) handlerHealthz(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	fmt.Fprintln(w, "hidup")
}

// handlerReadyz adalah readiness probe: memeriksa dependency yang
// menentukan siap-tidaknya layanan menerima traffic.
func (a *Aplikasi) handlerReadyz(w http.ResponseWriter, r *http.Request) {
	if !a.db.Ping() {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusServiceUnavailable)
		json.NewEncoder(w).Encode(responsSiap{
			Status:   "tidak siap",
			Penyebab: "database tidak merespons",
		})
		return
	}
	w.WriteHeader(http.StatusOK)
	fmt.Fprintln(w, "siap")
}

func main() {
	db := &StatusDB{siap: true}
	app := &Aplikasi{db: db}

	// Wiring mux: dua endpoint terpisah dengan pola method + path.
	mux := http.NewServeMux()
	mux.HandleFunc("GET /healthz", app.handlerHealthz)
	mux.HandleFunc("GET /readyz", app.handlerReadyz)
	server := httptest.NewServer(mux)
	defer server.Close()

	for _, skenario := range []struct {
		nama string
		siap bool
	}{
		{"database merespons", true},
		{"database mati", false},
	} {
		fmt.Printf("--- %s ---\n", skenario.nama)
		db.SetSiap(skenario.siap)
		for _, endpoint := range []string{"/healthz", "/readyz"} {
			resp, err := http.Get(server.URL + endpoint)
			if err != nil {
				panic(err)
			}
			isi, _ := io.ReadAll(resp.Body)
			resp.Body.Close()
			fmt.Printf("GET %-8s -> %d %s", endpoint, resp.StatusCode, string(isi))
		}
	}
}
