package middlewares

import (
	"net/http"
	"strconv"
	"time"
	"unicode/utf8"

	"github.com/containous/traefik/log"
	"github.com/containous/traefik/metrics"
	gokitmetrics "github.com/go-kit/kit/metrics"
)

// MetricsWrapper is a Negroni compatible Handler which relies on a
// given Metrics implementation to expose and monitor Traefik Metrics.
type MetricsWrapper struct {
	registry    metrics.Registry
	serviceName string
}

// NewMetricsWrapper return a MetricsWrapper struct with
// a given Metrics implementation
func NewMetricsWrapper(registry metrics.Registry, service string) *MetricsWrapper {
	var metricsWrapper = MetricsWrapper{
		registry:    registry,
		serviceName: service,
	}

	return &metricsWrapper
}

func (m *MetricsWrapper) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	start := time.Now()
	prw := &responseRecorder{rw, http.StatusOK}
	next(prw, r)

	reqLabels := []string{"service", m.serviceName, "code", strconv.Itoa(prw.statusCode), "method", getMethod(r)}
	m.registry.ReqsCounter().With(reqLabels...).Add(1)

	reqDurationLabels := []string{"service", m.serviceName, "code", strconv.Itoa(prw.statusCode)}
	m.registry.ReqDurationHistogram().With(reqDurationLabels...).Observe(float64(time.Since(start).Seconds()))
}

type retryMetrics interface {
	RetriesCounter() gokitmetrics.Counter
}

// NewMetricsRetryListener instantiates a MetricsRetryListener with the given retryMetrics.
func NewMetricsRetryListener(retryMetrics retryMetrics, backendName string) RetryListener {
	return &MetricsRetryListener{retryMetrics: retryMetrics, backendName: backendName}
}

func getMethod(r *http.Request) string {
	if !utf8.ValidString(r.Method) {
		log.Warnf("Invalid HTTP method encoding: %s", r.Method)
		return "NON_UTF8_HTTP_METHOD"
	}
	return r.Method
}

// MetricsRetryListener is an implementation of the RetryListener interface to
// record RequestMetrics about retry attempts.
type MetricsRetryListener struct {
	retryMetrics retryMetrics
	backendName  string
}

// Retried tracks the retry in the RequestMetrics implementation.
func (m *MetricsRetryListener) Retried(req *http.Request, attempt int) {
	m.retryMetrics.RetriesCounter().With("backend", m.backendName).Add(1)
}
