package http_round_tripper

import (
	"net/http"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

// Metric names for the recorded metrics.
// These are the conventional names prometheus uses for these metrics.
const (
	inFlightRequestsMetricName       = "in_flight_requests"
	requestsTotalMetricName          = "requests_total"
	requestDurationSecondsMetricName = "request_duration_seconds"
)

// Factory creates middleware instances. Created by NewFactory.
type Factory func(next http.RoundTripper, opts ...Option) http.RoundTripper

// NewFactory will create a function for creating metric middlewares.
// The resulting function can be called multiple times to obtain multiple middleware
// instances.
// Each instance can be configured with different options that will be applied to the
// same underlying metrics.
func NewFactory(opts ...FactoryOption) Factory {
	config := applyFactoryOptions(opts)

	inFlightRequests := prometheus.NewGauge(prometheus.GaugeOpts{
		Namespace: config.namespace,
		Subsystem: config.subsystem,
		Name:      inFlightRequestsMetricName,
		Help:      "A gauge of requests currently being handled.",
	})

	requestsTotal := prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Namespace: config.namespace,
			Subsystem: config.subsystem,
			Name:      requestsTotalMetricName,
			Help:      "A counter for total number of requests.",
		},
		config.labels,
	)

	requestDurationSeconds := prometheus.NewHistogramVec(
		prometheus.HistogramOpts{
			Namespace: config.namespace,
			Subsystem: config.subsystem,
			Name:      requestDurationSecondsMetricName,
			Help:      "A histogram of latencies for requests.",
			Buckets:   config.requestDurationBuckets,
		},
		config.labels,
	)

	prometheus.MustRegister(inFlightRequests, requestsTotal, requestDurationSeconds)

	return func(next http.RoundTripper, opts ...Option) http.RoundTripper {
		config := applyOptions(opts)

		rt := next

		rt = promhttp.InstrumentRoundTripperCounter(requestsTotal.MustCurryWith(config.labelValues), rt)
		rt = promhttp.InstrumentRoundTripperDuration(requestDurationSeconds.MustCurryWith(config.labelValues), rt)
		rt = promhttp.InstrumentRoundTripperInFlight(inFlightRequests, rt)

		return rt
	}
}
