package middleware

import (
	"strconv"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
)

var (
	// httpRequestsTotal counts total HTTP requests
	httpRequestsTotal = promauto.NewCounterVec(
		prometheus.CounterOpts{
			Name: "nexuscrm_http_requests_total",
			Help: "Total number of HTTP requests",
		},
		[]string{"method", "path", "status"},
	)

	// httpRequestDuration measures request latency
	httpRequestDuration = promauto.NewHistogramVec(
		prometheus.HistogramOpts{
			Name:    "nexuscrm_http_request_duration_seconds",
			Help:    "HTTP request latency in seconds",
			Buckets: prometheus.DefBuckets,
		},
		[]string{"method", "path"},
	)

	// httpRequestsInFlight tracks concurrent requests
	httpRequestsInFlight = promauto.NewGauge(
		prometheus.GaugeOpts{
			Name: "nexuscrm_http_requests_in_flight",
			Help: "Number of HTTP requests currently being processed",
		},
	)

	// dbQueryDuration measures database query latency
	DBQueryDuration = promauto.NewHistogramVec(
		prometheus.HistogramOpts{
			Name:    "nexuscrm_db_query_duration_seconds",
			Help:    "Database query latency in seconds",
			Buckets: []float64{0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1},
		},
		[]string{"operation", "table"},
	)

	// eventBusPublished counts events published
	EventBusPublished = promauto.NewCounterVec(
		prometheus.CounterOpts{
			Name: "nexuscrm_eventbus_published_total",
			Help: "Total number of events published to event bus",
		},
		[]string{"event_type"},
	)

	// eventBusDropped counts events dropped due to queue saturation
	EventBusDropped = promauto.NewCounterVec(
		prometheus.CounterOpts{
			Name: "nexuscrm_eventbus_dropped_total",
			Help: "Total number of events dropped due to queue saturation",
		},
		[]string{"event_type"},
	)

	// eventBusQueueUtilization tracks current queue utilization
	EventBusQueueUtilization = promauto.NewGauge(
		prometheus.GaugeOpts{
			Name: "nexuscrm_eventbus_queue_utilization",
			Help: "Current number of async events in the queue",
		},
	)

	// panicRecoveredTotal counts panics caught by the recovery middleware
	panicRecoveredTotal = promauto.NewCounter(
		prometheus.CounterOpts{
			Name: "nexuscrm_panic_recovered_total",
			Help: "Total number of panics caught by the recovery middleware.",
		},
	)
)

// RecordPanicRecovered increments the panic counter. Called by recovery middleware.
func RecordPanicRecovered() {
	panicRecoveredTotal.Inc()
}

// PrometheusMiddleware records HTTP request metrics
func PrometheusMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		start := time.Now()

		// Track in-flight requests
		httpRequestsInFlight.Inc()
		defer httpRequestsInFlight.Dec()

		// Process request
		c.Next()

		// Record metrics after request completes
		duration := time.Since(start).Seconds()
		status := strconv.Itoa(c.Writer.Status())
		path := c.FullPath()
		if path == "" {
			path = "unknown"
		}

		httpRequestsTotal.WithLabelValues(c.Request.Method, path, status).Inc()
		httpRequestDuration.WithLabelValues(c.Request.Method, path).Observe(duration)
	}
}

// RecordEventDropped records a dropped event due to queue saturation
func RecordEventDropped(eventType string) {
	EventBusDropped.WithLabelValues(eventType).Inc()
}

// UpdateEventBusQueueUtilization updates the current queue utilization
func UpdateEventBusQueueUtilization(current float64) {
	EventBusQueueUtilization.Set(current)
}

// GetHTTPRequestsTotalCount returns the cumulative HTTP request count across all status codes.
func GetHTTPRequestsTotalCount() int64 {
	var total int64
	mfs, err := prometheus.DefaultGatherer.Gather()
	if err != nil {
		return -1
	}
	for _, mf := range mfs {
		if mf.GetName() != "nexuscrm_http_requests_total" {
			continue
		}
		for _, m := range mf.Metric {
			total += int64(m.Counter.GetValue())
		}
	}
	return total
}

// GetHTTP5xxCount returns the current total of HTTP responses with status >= 500.
// Sums the Prometheus counter's children whose `status` label is 500..599.
func GetHTTP5xxCount() int64 {
	var total int64
	mfs, err := prometheus.DefaultGatherer.Gather()
	if err != nil {
		return -1
	}
	for _, mf := range mfs {
		if mf.GetName() != "nexuscrm_http_requests_total" {
			continue
		}
		for _, m := range mf.Metric {
			for _, lbl := range m.Label {
				if lbl.GetName() == "status" {
					if code, err := strconv.Atoi(lbl.GetValue()); err == nil && code >= 500 && code < 600 {
						total += int64(m.Counter.GetValue())
					}
				}
			}
		}
	}
	return total
}

// GetPanicRecoveredCount returns the current total of recovered panics.
func GetPanicRecoveredCount() int64 {
	mfs, err := prometheus.DefaultGatherer.Gather()
	if err != nil {
		return -1
	}
	for _, mf := range mfs {
		if mf.GetName() == "nexuscrm_panic_recovered_total" {
			for _, m := range mf.Metric {
				return int64(m.Counter.GetValue())
			}
		}
	}
	return 0
}
