package observability

import (
	"net"
	"strconv"

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

var (
	// Variables that are to be registered with prometheus. promauto automactically registers
	// the variable with prometheus. If promauto is not used variable needs to be registered
	// using prometheus.register. The following variable is of type Gauge metric.
	EtcdClusterHealth = promauto.NewGaugeVec(
		prometheus.GaugeOpts{
			Name: "edge_etcd_manager_cluster_health",
			Help: "Whether the etcd cluster is healthy and has quorum",
		}, []string{})
	EtcdAlarmStateCorrupt = promauto.NewGaugeVec(
		prometheus.GaugeOpts{
			Name: "edge_etcd_alarm_state_corrupt",
			Help: "Whether the etcd cluster has a corrupt alarm raised or not",
		}, []string{})
	EtcdAlarmStateNospace = promauto.NewGaugeVec(
		prometheus.GaugeOpts{
			Name: "edge_etcd_alarm_state_nospace",
			Help: "Whether the etcd cluster has a nospace alarm raised or not",
		}, []string{})
)

var metricsEndpoint = "/metrics"

type Server struct {
	Router *gin.Engine
	Port   int
}

func init() {
	gin.SetMode(gin.ReleaseMode)
	gin.DisableConsoleColor()
}

// NewServer creates an etcdmanager metrics server that runs on the desired port.
func NewServer(port int) *Server {
	router := gin.New()
	server := NewServerBuilder().WithRouter(router).WithPort(port).Build()

	metricsHandlerFunc := gin.WrapH(promhttp.Handler())
	server.Router.GET(metricsEndpoint, metricsHandlerFunc)

	return server
}

// Run runs the metrics server on the configured port
func (s *Server) Run() error {
	addr := net.JoinHostPort("", strconv.Itoa(s.Port))
	return s.Router.Run(addr)
}

// Builder Object for Server
type ServerBuilder struct {
	Router *gin.Engine
	Port   int
}

// Constructor for ServerBuilder
func NewServerBuilder() *ServerBuilder {
	o := new(ServerBuilder)
	return o
}

// Build Method which creates Server
func (b *ServerBuilder) Build() *Server {
	o := new(Server)
	o.Router = b.Router
	o.Port = b.Port
	return o
}

// WithRouter method sets the Router for the ServerBuilder
func (b *ServerBuilder) WithRouter(router *gin.Engine) *ServerBuilder {
	b.Router = router
	return b
}

// WithPort method sets the Port for the ServerBuilder
func (b *ServerBuilder) WithPort(port int) *ServerBuilder {
	b.Port = port
	return b
}