package ingestor

import (
	"sync"
	"time"

	"github.com/tech/sendico/fx/ingestor/internal/config"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
)

type serviceMetrics struct {
	pollDuration   *prometheus.HistogramVec
	pollTotal      *prometheus.CounterVec
	pairDuration   *prometheus.HistogramVec
	pairTotal      *prometheus.CounterVec
	pairLastUpdate *prometheus.GaugeVec
}

var (
	metricsOnce      sync.Once
	globalMetricsRef *serviceMetrics
)

func getServiceMetrics() *serviceMetrics {
	metricsOnce.Do(func() {
		reg := prometheus.DefaultRegisterer
		globalMetricsRef = &serviceMetrics{
			pollDuration: promauto.With(reg).NewHistogramVec(prometheus.HistogramOpts{
				Name:    "fx_ingestor_poll_duration_seconds",
				Help:    "Duration of a polling cycle.",
				Buckets: prometheus.DefBuckets,
			}, []string{"result"}),
			pollTotal: promauto.With(reg).NewCounterVec(prometheus.CounterOpts{
				Name: "fx_ingestor_poll_total",
				Help: "Total polling cycles executed.",
			}, []string{"result"}),
			pairDuration: promauto.With(reg).NewHistogramVec(prometheus.HistogramOpts{
				Name:    "fx_ingestor_pair_duration_seconds",
				Help:    "Duration of individual pair ingestion.",
				Buckets: prometheus.DefBuckets,
			}, []string{"source", "provider", "symbol", "result"}),
			pairTotal: promauto.With(reg).NewCounterVec(prometheus.CounterOpts{
				Name: "fx_ingestor_pair_total",
				Help: "Total ingestion attempts per pair.",
			}, []string{"source", "provider", "symbol", "result"}),
			pairLastUpdate: promauto.With(reg).NewGaugeVec(prometheus.GaugeOpts{
				Name: "fx_ingestor_pair_last_success_unix",
				Help: "Unix timestamp of the last successful ingestion per pair.",
			}, []string{"source", "provider", "symbol"}),
		}
	})
	return globalMetricsRef
}

func (m *serviceMetrics) observePoll(duration time.Duration, err error) {
	if m == nil {
		return
	}
	result := labelForError(err)
	m.pollDuration.WithLabelValues(result).Observe(duration.Seconds())
	m.pollTotal.WithLabelValues(result).Inc()
}

func (m *serviceMetrics) observePair(pair config.Pair, duration time.Duration, err error) {
	if m == nil {
		return
	}
	result := labelForError(err)
	labels := []string{pair.Source.String(), pair.Provider, pair.Symbol, result}
	m.pairDuration.WithLabelValues(labels...).Observe(duration.Seconds())
	m.pairTotal.WithLabelValues(labels...).Inc()
	if err == nil {
		m.pairLastUpdate.WithLabelValues(pair.Source.String(), pair.Provider, pair.Symbol).
			Set(float64(time.Now().Unix()))
	}
}

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