package grpcapi

import (
	"net/http"
)

// withCORS wraps next so that CORS preflight (OPTIONS) requests with an
// allow-listed Origin receive 204 plus the `Access-Control-Allow-*`
// headers Connect-Web needs, and actual requests get the matching
// `Access-Control-Allow-Origin` header echoed back. Origins are
// compared exactly: scheme, host, and port must match. An empty
// allow-list passes through untouched — the production posture when
// the UI and the gateway share one hostname.
//
// The wrapper mirrors `restapi.withCORS` but speaks plain `net/http`
// because the Connect handler is mounted on a `http.ServeMux`, not a
// gin engine. Connect-Web POSTs use `Content-Type: application/connect+json`
// which triggers a browser preflight; without these headers the
// browser surfaces "Load failed" before the Connect handler even sees
// the request.
func withCORS(allowedOrigins []string, next http.Handler) http.Handler {
	allowed := make(map[string]struct{}, len(allowedOrigins))
	for _, origin := range allowedOrigins {
		allowed[origin] = struct{}{}
	}
	if len(allowed) == 0 {
		return next
	}
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		origin := r.Header.Get("Origin")
		if origin == "" {
			next.ServeHTTP(w, r)
			return
		}
		if _, ok := allowed[origin]; !ok {
			next.ServeHTTP(w, r)
			return
		}
		w.Header().Set("Access-Control-Allow-Origin", origin)
		w.Header().Add("Vary", "Origin")
		w.Header().Set("Access-Control-Allow-Credentials", "true")
		if r.Method == http.MethodOptions {
			w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS")
			if reqHeaders := r.Header.Get("Access-Control-Request-Headers"); reqHeaders != "" {
				w.Header().Set("Access-Control-Allow-Headers", reqHeaders)
			} else {
				// Defaults cover the Connect-Web preflight set: protocol
				// version, content type, timeout, and the signed-request
				// metadata the gateway interceptor expects.
				w.Header().Set("Access-Control-Allow-Headers",
					"Content-Type, Connect-Protocol-Version, Connect-Timeout-Ms, Authorization")
			}
			// Expose the response headers Connect-Web needs to read on
			// the client (e.g. trailers folded into headers for unary).
			w.Header().Set("Access-Control-Expose-Headers", "Connect-Protocol-Version, Grpc-Status, Grpc-Message")
			w.Header().Set("Access-Control-Max-Age", "3600")
			w.WriteHeader(http.StatusNoContent)
			return
		}
		// Expose the same response headers on the actual call.
		w.Header().Set("Access-Control-Expose-Headers", "Connect-Protocol-Version, Grpc-Status, Grpc-Message")
		next.ServeHTTP(w, r)
	})
}
