package main

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"net/http/httptest"
	"sync/atomic"
)

type Adapter struct{ Client *http.Client }

// One application-level Do call, with no status-code retry here.
// Transport retries and redirects are separate behavior.
func (a Adapter) Call(ctx context.Context, url, key string) (int, error) {
	r, e := http.NewRequestWithContext(ctx, http.MethodPost, url, nil)
	if e != nil {
		return 0, e
	}
	r.Header.Set("Idempotency-Key", key)
	res, e := a.Client.Do(r)
	if e != nil {
		return 0, e
	}
	defer res.Body.Close()
	return res.StatusCode, nil
}
func main() {
	var attempts atomic.Int64
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		attempts.Add(1)
		if r.Header.Get("Idempotency-Key") != "o1" {
			w.WriteHeader(400)
			return
		}
		w.WriteHeader(503)
	}))
	defer srv.Close()
	a := Adapter{srv.Client()}
	status, err := a.Call(context.Background(), srv.URL, "o1")
	fmt.Println("status:", status, "error:", err, "attempts:", attempts.Load())
	ctx, cancel := context.WithCancel(context.Background())
	cancel()
	_, err = a.Call(ctx, srv.URL, "o2")
	fmt.Println("cancel propagated:", errors.Is(err, context.Canceled), "attempts:", attempts.Load())
}
