// Package metricsserver provides a standalone HTTP server for exposing Prometheus metrics.
//
// This package implements a dedicated metrics server that exposes application metrics
// via HTTP. It uses a custom Prometheus registry to avoid conflicts with other metric
// collectors and provides graceful shutdown capabilities.
package metricsserver

import (
	"context"
	"net/http"
	"strconv"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"golang.org/x/sync/errgroup"

	"go.ntppool.org/common/logger"
)

// Metrics provides a custom Prometheus registry and HTTP handlers for metrics exposure.
// It isolates application metrics from the default global registry.
type Metrics struct {
	r *prometheus.Registry
}

// New creates a new Metrics instance with a custom Prometheus registry.
func New() *Metrics {
	r := prometheus.NewRegistry()

	m := &Metrics{
		r: r,
	}

	return m
}

// Registry returns the custom Prometheus registry.
// Use this to register your application's metrics collectors.
func (m *Metrics) Registry() *prometheus.Registry {
	return m.r
}

// Handler returns an HTTP handler for the /metrics endpoint with OpenMetrics support.
func (m *Metrics) Handler() http.Handler {
	log := logger.NewStdLog("prom http", false, nil)

	return promhttp.HandlerFor(m.r, promhttp.HandlerOpts{
		ErrorLog:          log,
		Registry:          m.r,
		EnableOpenMetrics: true,
	})
}

// ListenAndServe starts a metrics server on the specified port and blocks until ctx is done.
// The server exposes the metrics handler and shuts down gracefully when the context is cancelled.
func (m *Metrics) ListenAndServe(ctx context.Context, port int) error {
	log := logger.Setup()

	srv := &http.Server{
		Addr:         ":" + strconv.Itoa(port),
		ReadTimeout:  10 * time.Second,
		WriteTimeout: 20 * time.Second,
		IdleTimeout:  120 * time.Second,
		Handler:      m.Handler(),
	}

	g, ctx := errgroup.WithContext(ctx)

	g.Go(func() error {
		err := srv.ListenAndServe()
		if err != nil && err != http.ErrServerClosed {
			log.Warn("metrics server done listening", "err", err)
			return err
		}
		return nil
	})

	<-ctx.Done()

	if err := srv.Shutdown(ctx); err != nil {
		log.Error("metrics server shutdown failed", "err", err)
		return err
	}

	return g.Wait()
}