package metrics

import (
	"net/http"
	"strconv"
	"time"
)

// responseWriter wraps http.ResponseWriter to capture the status code written
// by downstream handlers.
type responseWriter struct {
	http.ResponseWriter
	status int
}

func (rw *responseWriter) WriteHeader(code int) {
	rw.status = code
	rw.ResponseWriter.WriteHeader(code)
}

// statusCode returns the captured status code, defaulting to 200 if WriteHeader
// was never called (Go's default behaviour).
func (rw *responseWriter) statusCode() string {
	if rw.status == 0 {
		return "200"
	}
	return strconv.Itoa(rw.status)
}

// Middleware records request_count, request_duration, and request_errors for
// every HTTP request. It must be added to the chi middleware chain after
// RequestID but before the route handlers.
func Middleware(provider string) func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			start := time.Now()
			wrapped := &responseWriter{ResponseWriter: w}

			next.ServeHTTP(wrapped, r)

			status := wrapped.statusCode()
			duration := time.Since(start).Seconds()
			path := r.URL.Path

			RequestsTotal.WithLabelValues(r.Method, path, status, provider).Inc()
			RequestDuration.WithLabelValues(r.Method, path, status).Observe(duration)

			// Count 4xx/5xx as errors.
			code := wrapped.status
			if code == 0 {
				code = http.StatusOK
			}
			if code >= 400 {
				errorType := "server_error"
				if code == http.StatusUnauthorized {
					errorType = "authentication_error"
				} else if code == http.StatusTooManyRequests {
					errorType = "rate_limit_error"
				} else if code < 500 {
					errorType = "client_error"
				}
				RequestErrors.WithLabelValues(r.Method, path, errorType).Inc()
			}
		})
	}
}
