package metrics

import (
	"time"

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

// PoolStat represents MySQL pool statistics for metrics export.
type PoolStat struct {
	TenantID     string
	User         string
	KeyspaceName string
	ClusterID    string
	Open         int
	InUse        int
	WaitCount    int64
	WaitDuration time.Duration
}

// PoolStatsProvider returns a snapshot of pool stats.
type PoolStatsProvider func() []PoolStat

type poolStatsCollector struct {
	openDesc         *prometheus.Desc
	inUseDesc        *prometheus.Desc
	waitCountDesc    *prometheus.Desc
	waitDurationDesc *prometheus.Desc
}

func newPoolStatsCollector() *poolStatsCollector {
	labels := []string{"tenant", "user", "KeyspaceName", "ClusterId"}
	return &poolStatsCollector{
		openDesc: prometheus.NewDesc(
			"proxy_mysql_pool_open",
			"Number of open connections in the MySQL pool.",
			labels,
			nil,
		),
		inUseDesc: prometheus.NewDesc(
			"proxy_mysql_pool_in_use",
			"Number of in-use connections in the MySQL pool.",
			labels,
			nil,
		),
		waitCountDesc: prometheus.NewDesc(
			"proxy_mysql_pool_wait_total",
			"Total count of MySQL pool waits.",
			labels,
			nil,
		),
		waitDurationDesc: prometheus.NewDesc(
			"proxy_mysql_pool_wait_seconds_total",
			"Total time spent waiting for MySQL pool connections.",
			labels,
			nil,
		),
	}
}

func (c *poolStatsCollector) Describe(ch chan<- *prometheus.Desc) {
	ch <- c.openDesc
	ch <- c.inUseDesc
	ch <- c.waitCountDesc
	ch <- c.waitDurationDesc
}

func (c *poolStatsCollector) Collect(ch chan<- prometheus.Metric) {
	if c == nil {
		return
	}
	provider := activePoolStatsProvider()
	if provider == nil {
		return
	}
	stats := provider()
	for _, stat := range stats {
		metricLabels := MetricLabels{
			KeyspaceName: stat.KeyspaceName,
			ClusterID:    stat.ClusterID,
		}.withDefaults()
		if stat.User != "" && (metricLabels.KeyspaceName == unknownKeyspaceName || metricLabels.ClusterID == unknownClusterID) {
			resolved := ResolveMetricLabels(nil, stat.User)
			if metricLabels.KeyspaceName == unknownKeyspaceName {
				metricLabels.KeyspaceName = resolved.KeyspaceName
			}
			if metricLabels.ClusterID == unknownClusterID {
				metricLabels.ClusterID = resolved.ClusterID
			}
		}
		labels := []string{stat.TenantID, stat.User, metricLabels.KeyspaceName, metricLabels.ClusterID}
		ch <- prometheus.MustNewConstMetric(c.openDesc, prometheus.GaugeValue, float64(stat.Open), labels...)
		ch <- prometheus.MustNewConstMetric(c.inUseDesc, prometheus.GaugeValue, float64(stat.InUse), labels...)
		ch <- prometheus.MustNewConstMetric(c.waitCountDesc, prometheus.GaugeValue, float64(stat.WaitCount), labels...)
		ch <- prometheus.MustNewConstMetric(c.waitDurationDesc, prometheus.GaugeValue, stat.WaitDuration.Seconds(), labels...)
	}
}
