package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
)

type Item struct {
	Nama  string `json:"nama"`
	Harga int    `json:"harga"`
}

type HasilCari struct {
	Kata  string   `json:"kata"`
	Tags  []string `json:"tags"`
	ReqID string   `json:"reqID"`
}

type Balasan struct {
	Pesan string `json:"pesan"`
	Nama  string `json:"nama"`
}

// writeJSON mengatur header, status code, lalu menulis body JSON.
func writeJSON(w http.ResponseWriter, status int, data any) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	json.NewEncoder(w).Encode(data)
}

func main() {
	mux := http.NewServeMux()

	// Membaca query parameter (satu nilai dan multi nilai) + header.
	mux.HandleFunc("GET /cari", func(w http.ResponseWriter, r *http.Request) {
		q := r.URL.Query()
		hasil := HasilCari{
			Kata:  q.Get("q"),
			Tags:  q["tag"],
			ReqID: r.Header.Get("X-Request-Id"),
		}
		writeJSON(w, http.StatusOK, hasil)
	})

	// Membaca body JSON dengan pembatas ukuran dari MaxBytesReader.
	mux.HandleFunc("POST /simpan", func(w http.ResponseWriter, r *http.Request) {
		r.Body = http.MaxBytesReader(w, r.Body, 16)
		var item Item
		if err := json.NewDecoder(r.Body).Decode(&item); err != nil {
			http.Error(w, "body tidak valid: "+err.Error(), http.StatusBadRequest)
			return
		}
		writeJSON(w, http.StatusCreated, Balasan{Pesan: "item tersimpan", Nama: item.Nama})
	})

	// Membaca form application/x-www-form-urlencoded dengan r.FormValue.
	mux.HandleFunc("POST /daftar", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "terdaftar: %s <%s>", r.FormValue("nama"), r.FormValue("email"))
	})

	// GET /cari dengan query multi-value dan header custom.
	req := httptest.NewRequest("GET", "/cari?q=go&tag=buku&tag=net", nil)
	req.Header.Set("X-Request-Id", "req-42")
	uji(mux, req)

	// POST /simpan dengan body JSON 15 byte (masih di bawah batas).
	uji(mux, httptest.NewRequest("POST", "/simpan",
		strings.NewReader(`{"nama":"Buku"}`)))

	// POST /simpan dengan body 32 byte: melebihi batas MaxBytesReader.
	uji(mux, httptest.NewRequest("POST", "/simpan",
		strings.NewReader(`{"nama":"Buku Go","harga":50000}`)))

	// POST /daftar dengan form + query yang bernama sama.
	form := strings.NewReader("nama=Budi&email=budi%40example.com")
	reqDaftar := httptest.NewRequest("POST", "/daftar?email=url@example.com", form)
	reqDaftar.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	uji(mux, reqDaftar)
}

func uji(h http.Handler, req *http.Request) {
	rec := httptest.NewRecorder()
	h.ServeHTTP(rec, req)
	fmt.Printf("%s %s -> %d (%s)\n", req.Method, req.RequestURI, rec.Code,
		rec.Header().Get("Content-Type"))
	fmt.Printf("  body: %s\n", strings.TrimSpace(rec.Body.String()))
}
