package apimetrics

import (
	"strconv"
	"strings"
	"sync"
	"time"

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

var (
	httpRequestsTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
		Namespace: "diagnosis_query",
		Name:      "http_requests_total",
		Help:      "HTTP requests to diagnosis-query /api/v1 handlers.",
	}, []string{"method", "path", "status_code", "api"})

	httpRequestDurationSeconds = prometheus.NewHistogramVec(prometheus.HistogramOpts{
		Namespace: "diagnosis_query",
		Name:      "http_request_duration_seconds",
		Help:      "HTTP request latency for diagnosis-query /api/v1 handlers.",
		Buckets:   []float64{0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30, 60, 120, 180},
	}, []string{"method", "path", "status_code", "api"})

	once sync.Once
)

// Init registers Prometheus metrics. Idempotent.
func Init() {
	once.Do(func() {
		prometheus.MustRegister(httpRequestsTotal, httpRequestDurationSeconds)
	})
}

// Middleware records per-route request count and latency for /api/v1.
// Path labels use gin FullPath (route template) to keep cardinality bounded.
func Middleware() gin.HandlerFunc {
	Init()
	return func(c *gin.Context) {
		start := time.Now()
		c.Next()

		path := c.FullPath()
		if path == "" {
			// Unmatched / health-style noise — skip to avoid high-cardinality raw URLs.
			return
		}
		api := apiGroupFromPath(path)
		method := c.Request.Method
		status := strconv.Itoa(c.Writer.Status())

		httpRequestsTotal.WithLabelValues(method, path, status, api).Inc()
		httpRequestDurationSeconds.WithLabelValues(method, path, status, api).Observe(time.Since(start).Seconds())
	}
}

func apiGroupFromPath(fullPath string) string {
	p := strings.TrimPrefix(fullPath, "/api/v1")
	p = strings.TrimPrefix(p, "/")
	if p == "" {
		return "root"
	}
	seg := p
	if i := strings.IndexByte(p, '/'); i >= 0 {
		seg = p[:i]
	}
	switch strings.ToLower(seg) {
	case "statements", "sqls", "slow_query", "topsql", "topru", "olap",
		"continuous_profiling", "keyviz", "keyvisual":
		return strings.ToLower(seg)
	default:
		return seg
	}
}
