package documents

import (
	"strings"
	"sync"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

var (
	metricsOnce sync.Once

	requestsTotal  *prometheus.CounterVec
	requestLatency *prometheus.HistogramVec
	documentBytes  *prometheus.HistogramVec
)

func initMetrics() {
	metricsOnce.Do(func() {
		requestsTotal = promauto.NewCounterVec(
			prometheus.CounterOpts{
				Namespace: "billing",
				Subsystem: "documents",
				Name:      "requests_total",
				Help:      "Total number of billing document requests processed.",
			},
			[]string{"call", "status", "doc_type"},
		)

		requestLatency = promauto.NewHistogramVec(
			prometheus.HistogramOpts{
				Namespace: "billing",
				Subsystem: "documents",
				Name:      "request_latency_seconds",
				Help:      "Latency of billing document requests.",
				Buckets:   prometheus.DefBuckets,
			},
			[]string{"call", "status", "doc_type"},
		)

		documentBytes = promauto.NewHistogramVec(
			prometheus.HistogramOpts{
				Namespace: "billing",
				Subsystem: "documents",
				Name:      "document_bytes",
				Help:      "Size of generated billing document payloads.",
				Buckets:   prometheus.ExponentialBuckets(1024, 2, 10),
			},
			[]string{"doc_type"},
		)
	})
}

func observeRequest(call string, documentKind, statusLabel string, took time.Duration) {
	kind := docKindLabel(documentKind)
	requestsTotal.WithLabelValues(call, statusLabel, kind).Inc()
	requestLatency.WithLabelValues(call, statusLabel, kind).Observe(took.Seconds())
}

func observeDocumentBytes(documentKind string, size int) {
	documentBytes.WithLabelValues(docKindLabel(documentKind)).Observe(float64(size))
}

func statusFromError(err error) string {
	if err == nil {
		return "success"
	}

	st, ok := status.FromError(err)
	if !ok {
		return "error"
	}

	code := st.Code()

	if code == codes.OK {
		return "success"
	}

	return strings.ToLower(code.String())
}

func docKindLabel(documentKind string) string {
	label := strings.TrimSpace(documentKind)
	if label == "" {
		return "operation"
	}

	return label
}
