package main

import (
	"context"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"time"
)

type kunciRequestID struct{}

func main() {
	// Bagian 1: handler merespons pembatalan yang datang dari klien.
	hasil := make(chan string, 1)

	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		for langkah := 1; langkah <= 10; langkah++ {
			select {
			case <-ctx.Done():
				hasil <- "dibatalkan"
				return
			case <-time.After(20 * time.Millisecond):
				// satu langkah kerja selesai, lanjut ke langkah berikutnya
			}
		}
		hasil <- "selesai"
		fmt.Fprintln(w, "respons penuh")
	}))
	defer ts.Close()

	klien := &http.Client{Timeout: 2 * time.Second}

	// permintaan normal: handler dibiarkan menyelesaikan seluruh kerjanya
	resp, err := klien.Get(ts.URL)
	if err != nil {
		fmt.Println("permintaan normal gagal:", err)
		return
	}
	isi, _ := io.ReadAll(resp.Body)
	resp.Body.Close()
	fmt.Println("permintaan normal:", resp.Status, "isi:", strings.TrimSpace(string(isi)))
	fmt.Println("pandangan handler:", <-hasil)

	// permintaan yang dibatalkan setelah 50 milidetik
	ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
	defer cancel()

	req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
	if err != nil {
		fmt.Println("gagal membuat permintaan:", err)
		return
	}
	_, err = klien.Do(req)
	fmt.Println("permintaan dibatalkan klien:", err != nil)
	fmt.Println("error bertipe deadline:", errors.Is(err, context.DeadlineExceeded))
	fmt.Println("pandangan handler:", <-hasil)

	// Bagian 2: nilai context mengalir dari middleware ke handler.
	middleware := func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			ctx := context.WithValue(r.Context(), kunciRequestID{}, "req-001")
			next.ServeHTTP(w, r.WithContext(ctx))
		})
	}

	tsNilai := httptest.NewServer(middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		id, _ := r.Context().Value(kunciRequestID{}).(string)
		fmt.Fprintf(w, "request id: %s", id)
	})))
	defer tsNilai.Close()

	resp2, err := klien.Get(tsNilai.URL)
	if err != nil {
		fmt.Println("permintaan nilai gagal:", err)
		return
	}
	isi2, _ := io.ReadAll(resp2.Body)
	resp2.Body.Close()
	fmt.Println("nilai request id terbaca handler:", strings.TrimSpace(string(isi2)))
}
