package metrics

import (
	"database/sql"
	"time"

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

var (
	// dbPoolOpenConnections mesure le nombre de connexions ouvertes dans le pool
	dbPoolOpenConnections = promauto.NewGauge(
		prometheus.GaugeOpts{
			Name: "veza_db_pool_open_connections",
			Help: "Number of open database connections in the pool",
		},
	)

	// dbPoolInUse mesure le nombre de connexions en cours d'utilisation
	dbPoolInUse = promauto.NewGauge(
		prometheus.GaugeOpts{
			Name: "veza_db_pool_in_use",
			Help: "Number of database connections currently in use",
		},
	)

	// dbPoolIdle mesure le nombre de connexions inactives
	dbPoolIdle = promauto.NewGauge(
		prometheus.GaugeOpts{
			Name: "veza_db_pool_idle",
			Help: "Number of idle database connections in the pool",
		},
	)

	// dbPoolWaitCount mesure le nombre total de fois qu'une connexion a dû attendre
	// Note: sql.DBStats.WaitCount est cumulatif depuis le démarrage, donc on utilise un Gauge
	// et on le met à jour avec la valeur absolue
	dbPoolWaitCount = promauto.NewGauge(
		prometheus.GaugeOpts{
			Name: "veza_db_pool_wait_count_total",
			Help: "Total number of times a connection had to wait (cumulative since startup)",
		},
	)

	// dbPoolWaitDuration mesure la durée totale d'attente pour obtenir une connexion
	// Note: sql.DBStats.WaitDuration est cumulatif depuis le démarrage, donc on utilise un Gauge
	dbPoolWaitDuration = promauto.NewGauge(
		prometheus.GaugeOpts{
			Name: "veza_db_pool_wait_duration_seconds_total",
			Help: "Total time spent waiting for a database connection in seconds (cumulative since startup)",
		},
	)

	// dbPoolMaxIdleClosed mesure le nombre de connexions fermées à cause de MaxIdleConns
	// Note: sql.DBStats.MaxIdleClosed est cumulatif depuis le démarrage, donc on utilise un Gauge
	dbPoolMaxIdleClosed = promauto.NewGauge(
		prometheus.GaugeOpts{
			Name: "veza_db_pool_max_idle_closed_total",
			Help: "Total number of connections closed due to MaxIdleConns limit (cumulative since startup)",
		},
	)

	// dbPoolMaxIdleTimeClosed mesure le nombre de connexions fermées à cause de MaxIdleTime
	// Note: sql.DBStats.MaxIdleTimeClosed est cumulatif depuis le démarrage, donc on utilise un Gauge
	dbPoolMaxIdleTimeClosed = promauto.NewGauge(
		prometheus.GaugeOpts{
			Name: "veza_db_pool_max_idle_time_closed_total",
			Help: "Total number of connections closed due to MaxIdleTime limit (cumulative since startup)",
		},
	)

	// dbPoolMaxLifetimeClosed mesure le nombre de connexions fermées à cause de MaxLifetime
	// Note: sql.DBStats.MaxLifetimeClosed est cumulatif depuis le démarrage, donc on utilise un Gauge
	dbPoolMaxLifetimeClosed = promauto.NewGauge(
		prometheus.GaugeOpts{
			Name: "veza_db_pool_max_lifetime_closed_total",
			Help: "Total number of connections closed due to MaxLifetime limit (cumulative since startup)",
		},
	)
)

// UpdateDBPoolStats met à jour les métriques du pool de connexions DB
// MOD-P2-004: Expose les métriques database/sql stats via Prometheus
func UpdateDBPoolStats(stats sql.DBStats) {
	// Métriques de connexions actuelles
	dbPoolOpenConnections.Set(float64(stats.OpenConnections))
	dbPoolInUse.Set(float64(stats.InUse))
	dbPoolIdle.Set(float64(stats.Idle))

	// Métriques de wait (cumulatives depuis le démarrage)
	// Note: sql.DBStats fournit des valeurs cumulatives, donc on utilise Set pour mettre à jour les gauges
	dbPoolWaitCount.Set(float64(stats.WaitCount))
	dbPoolWaitDuration.Set(stats.WaitDuration.Seconds())

	// Métriques de connexions fermées (cumulatives depuis le démarrage)
	dbPoolMaxIdleClosed.Set(float64(stats.MaxIdleClosed))
	dbPoolMaxIdleTimeClosed.Set(float64(stats.MaxIdleTimeClosed))
	dbPoolMaxLifetimeClosed.Set(float64(stats.MaxLifetimeClosed))
}

// StartDBPoolStatsCollector démarre un collecteur périodique pour les métriques DB pool
// MOD-P2-004: Collecte les stats DB pool périodiquement et les expose via Prometheus
func StartDBPoolStatsCollector(db *sql.DB, interval time.Duration) {
	if db == nil {
		return
	}

	go func() {
		ticker := time.NewTicker(interval)
		defer ticker.Stop()

		// Collecter immédiatement
		UpdateDBPoolStats(db.Stats())

		// Collecter périodiquement
		for range ticker.C {
			UpdateDBPoolStats(db.Stats())
		}
	}()
}
