package middlewaredemo

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

type requestKey struct{}

var sequence atomic.Uint64

func requestID(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		id := fmt.Sprintf("local-%d", sequence.Add(1))
		w.Header().Set("X-Request-ID", id)
		next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), requestKey{}, id)))
	})
}
func cors(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		origin := r.Header.Get("Origin")
		w.Header().Add("Vary", "Origin")
		if origin != "" && origin != "https://app.example" {
			http.Error(w, "origin rejected", 403)
			return
		}
		if origin != "" {
			w.Header().Set("Access-Control-Allow-Origin", origin)
		}
		if r.Method == "OPTIONS" && r.Header.Get("Access-Control-Request-Method") != "" {
			w.Header().Add("Vary", "Access-Control-Request-Method")
			w.Header().Add("Vary", "Access-Control-Request-Headers")
			if origin == "" || r.Header.Get("Access-Control-Request-Method") != "GET" {
				http.Error(w, "preflight rejected", 403)
				return
			}
			for _, h := range strings.Split(r.Header.Get("Access-Control-Request-Headers"), ",") {
				h = strings.TrimSpace(h)
				if h != "" && !strings.EqualFold(h, "Authorization") {
					http.Error(w, "headers rejected", 403)
					return
				}
			}
			w.Header().Set("Access-Control-Allow-Methods", "GET")
			w.Header().Set("Access-Control-Allow-Headers", "Authorization")
			w.WriteHeader(204)
			return
		}
		next.ServeHTTP(w, r)
	})
}
func auth(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// A fixed, public test token is not a production authentication system.
		if r.Header.Get("Authorization") != "Bearer test-only" {
			w.Header().Set("WWW-Authenticate", `Bearer realm="example"`)
			http.Error(w, "authentication required", 401)
			return
		}
		next.ServeHTTP(w, r)
	})
}

type committedWriter struct {
	http.ResponseWriter
	committed bool
}

func (w *committedWriter) Unwrap() http.ResponseWriter { return w.ResponseWriter }
func (w *committedWriter) FlushError() error {
	if !w.committed {
		w.WriteHeader(200)
	}
	return http.NewResponseController(w.ResponseWriter).Flush()
}

func (w *committedWriter) WriteHeader(code int) {
	if code >= 200 || code == http.StatusSwitchingProtocols {
		w.committed = true
	}
	w.ResponseWriter.WriteHeader(code)
}
func (w *committedWriter) Write(p []byte) (int, error) {
	w.committed = true
	return w.ResponseWriter.Write(p)
}
func recoverBeforeCommit(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		cw := &committedWriter{ResponseWriter: w}
		defer func() {
			if p := recover(); p != nil {
				if p == http.ErrAbortHandler || cw.committed {
					panic(http.ErrAbortHandler)
				}
				// A real server logs the stack to a restricted sink, never to this response.
				http.Error(cw, "internal error", 500)
			}
		}()
		next.ServeHTTP(cw, r)
	})
}
func TestChain(t *testing.T) {
	var calls atomic.Int32
	endpoint := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		calls.Add(1)
		if r.Context().Value(requestKey{}) == nil {
			panic("missing request id")
		}
		w.WriteHeader(204)
	})
	h := requestID(cors(recoverBeforeCommit(auth(endpoint))))
	for _, tc := range []struct {
		origin, token string
		want          int
	}{
		{"https://app.example", "", 401}, {"", "Bearer test-only", 204},
		{"https://evil.example", "Bearer test-only", 403},
	} {
		r := httptest.NewRequest("GET", "/", nil)
		r.Header.Set("Origin", tc.origin)
		r.Header.Set("Authorization", tc.token)
		w := httptest.NewRecorder()
		h.ServeHTTP(w, r)
		if w.Code != tc.want || w.Header().Get("X-Request-ID") == "" {
			t.Fatalf("%d %v", w.Code, w.Header())
		}
	}
	r := httptest.NewRequest("OPTIONS", "/", nil)
	r.Header.Set("Origin", "https://app.example")
	r.Header.Set("Access-Control-Request-Method", "GET")
	w := httptest.NewRecorder()
	h.ServeHTTP(w, r)
	if w.Code != 204 || calls.Load() != 1 {
		t.Fatalf("status=%d endpoint calls=%d", w.Code, calls.Load())
	}
	t.Log("CORS allowed origin still needs auth; Origin-free client still needs auth; preflight does not call business handler")
}
func TestRecoveryBoundary(t *testing.T) {
	w := httptest.NewRecorder()
	recoverBeforeCommit(http.HandlerFunc(func(http.ResponseWriter, *http.Request) { panic("boom") })).ServeHTTP(w, httptest.NewRequest("GET", "/", nil))
	if w.Code != 500 {
		t.Fatal(w.Code)
	}
	defer func() {
		if p := recover(); p != http.ErrAbortHandler {
			t.Fatalf("post-commit panic=%v", p)
		}
	}()
	recoverBeforeCommit(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte("partial")); panic("boom") })).ServeHTTP(httptest.NewRecorder(), httptest.NewRequest("GET", "/", nil))
}
