package httpdemo

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

func Example_routes() {
	mux := http.NewServeMux()
	mux.HandleFunc("GET /users/{id}", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, r.PathValue("id"))
	})
	for _, method := range []string{"GET", "HEAD", "POST"} {
		w := httptest.NewRecorder()
		mux.ServeHTTP(w, httptest.NewRequest(method, "/users/42", nil))
		fmt.Printf("%s status=%d allow=%q\n", method, w.Code, w.Header().Get("Allow"))
	}
	// Output:
	// GET status=200 allow=""
	// HEAD status=200 allow=""
	// POST status=405 allow="GET, HEAD"
}

func Example_chain() {
	wrap := func(name string, next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			fmt.Println(name, "before")
			next.ServeHTTP(w, r)
			fmt.Println(name, "after")
		})
	}
	h := wrap("log", wrap("auth", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Println("handler") })))
	h.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest("GET", "/", nil))
	// Output:
	// log before
	// auth before
	// handler
	// auth after
	// log after
}

func TestCommittedResponse(t *testing.T) {
	w := httptest.NewRecorder()
	w.Header().Set("X-Phase", "before")
	_, _ = io.WriteString(w, "first")
	w.Header().Set("X-Phase", "after")
	w.WriteHeader(500)
	result := w.Result()
	defer result.Body.Close()
	if result.StatusCode != 200 || result.Header.Get("X-Phase") != "before" {
		t.Fatal(result)
	}
	t.Logf("status=%d header=%s body=%s", result.StatusCode, result.Header.Get("X-Phase"), w.Body.String())
}

func TestClientTimeoutIncludesBody(t *testing.T) {
	done := make(chan struct{})
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		defer close(done)
		_, _ = io.WriteString(w, "first\n")
		if err := http.NewResponseController(w).Flush(); err != nil {
			return
		}
		<-r.Context().Done()
	}))
	defer s.Close()
	c := &http.Client{Timeout: 200 * time.Millisecond}
	defer c.CloseIdleConnections()
	resp, err := c.Get(s.URL)
	if err != nil {
		t.Fatal(err)
	}
	body, err := io.ReadAll(resp.Body)
	resp.Body.Close()
	if !strings.HasPrefix(string(body), "first\n") || !errors.Is(err, context.DeadlineExceeded) {
		t.Fatalf("body=%q err=%v", body, err)
	}
	select {
	case <-done:
	case <-time.After(2 * time.Second):
		t.Fatal("server did not observe cancellation")
	}
	t.Logf("Do succeeded; body prefix=%q; deadline=%v", body, errors.Is(err, context.DeadlineExceeded))
}

func TestConflictingPatterns(t *testing.T) {
	defer func() {
		if recover() == nil {
			t.Fatal("conflicting routes must panic")
		}
		t.Log("conflict rejected at registration")
	}()
	mux := http.NewServeMux()
	mux.HandleFunc("/a/{x}", func(http.ResponseWriter, *http.Request) {})
	mux.HandleFunc("/{x}/b", func(http.ResponseWriter, *http.Request) {})
}
