package prometheus

import (
	"context"

	"github.com/micro/go-micro/v2/server"
	"github.com/prometheus/client_golang/prometheus"
)

var (
	// Namespace defines the namespace of the defined metrics.
	Namespace = "ocis"
)

// NewHandlerWrapper initializes the prometheus handler wrapper.
func NewHandlerWrapper(opts ...server.Option) server.HandlerWrapper {
	counter := prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Namespace: Namespace,
			Name:      "request_total",
			Help:      "How many service requests processed",
		},
		[]string{"method", "status"},
	)

	latency := prometheus.NewSummaryVec(
		prometheus.SummaryOpts{
			Namespace: Namespace,
			Name:      "upstream_latency_microseconds",
			Help:      "Service method latencies in microseconds",
		},
		[]string{"method"},
	)

	duration := prometheus.NewHistogramVec(
		prometheus.HistogramOpts{
			Namespace: Namespace,
			Name:      "request_duration_seconds",
			Help:      "Service method request time in seconds",
		},
		[]string{"method"},
	)

	prometheus.Register(
		prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{}),
	)

	prometheus.Register(
		prometheus.NewGoCollector(),
	)

	prometheus.Register(
		counter,
	)

	prometheus.Register(
		latency,
	)

	prometheus.Register(
		duration,
	)

	return func(fn server.HandlerFunc) server.HandlerFunc {
		return func(ctx context.Context, req server.Request, rsp interface{}) error {
			name := req.Endpoint()

			timer := prometheus.NewTimer(prometheus.ObserverFunc(func(v float64) {
				us := v * 1000000

				latency.WithLabelValues(name).Observe(us)
				duration.WithLabelValues(name).Observe(v)
			}))

			defer timer.ObserveDuration()

			err := fn(ctx, req, rsp)

			if err == nil {
				counter.WithLabelValues(name, "success").Inc()
			} else {
				counter.WithLabelValues(name, "fail").Inc()
			}

			return err
		}
	}
}
