package middleware

import (
	"crypto/rand"
	"encoding/hex"
	"net/http"
)

// RequestID injects a request ID into the context and response header.
// Writes X-Request-Id and enriches the request-scoped logger via logger.ToContext.
//
// A client-supplied X-Request-Id is only trusted when it matches a strict
// allowlist (printable, bounded, no CRLF/quote): otherwise it is discarded and a
// server-generated id is used. This prevents header and log injection (L4).
func RequestID(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		id := r.Header.Get("X-Request-Id")
		if !isValidRequestID(id) {
			id = newRequestID()
		}
		w.Header().Set("X-Request-Id", id)
		next.ServeHTTP(w, r.WithContext(contextWithRequestID(r.Context(), id)))
	})
}

// isValidRequestID accepts only short, safe, printable identifiers.
func isValidRequestID(id string) bool {
	if id == "" || len(id) > 64 {
		return false
	}
	for _, c := range id {
		switch {
		case c >= 'a' && c <= 'z', c >= 'A' && c <= 'Z', c >= '0' && c <= '9':
		case c == '-' || c == '_' || c == '.':
		default:
			// Reject control chars, whitespace, quotes, backslash, CRLF, etc.
			return false
		}
	}
	return true
}

func newRequestID() string {
	b := make([]byte, 16)
	if _, err := rand.Read(b); err != nil {
		return "unknown"
	}
	return hex.EncodeToString(b)
}
