package relay

import (
	

	
	pbv2 
	
)

const metricNamespace = "libp2p_relaysvc"

var (
	status = prometheus.NewGauge(
		prometheus.GaugeOpts{
			Namespace: metricNamespace,
			Name:      "status",
			Help:      "Relay Status",
		},
	)

	reservationsTotal = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Namespace: metricNamespace,
			Name:      "reservations_total",
			Help:      "Relay Reservation Request",
		},
		[]string{"type"},
	)
	reservationRequestResponseStatusTotal = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Namespace: metricNamespace,
			Name:      "reservation_request_response_status_total",
			Help:      "Relay Reservation Request Response Status",
		},
		[]string{"status"},
	)
	reservationRejectionsTotal = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Namespace: metricNamespace,
			Name:      "reservation_rejections_total",
			Help:      "Relay Reservation Rejected Reason",
		},
		[]string{"reason"},
	)

	connectionsTotal = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Namespace: metricNamespace,
			Name:      "connections_total",
			Help:      "Relay Connection Total",
		},
		[]string{"type"},
	)
	connectionRequestResponseStatusTotal = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Namespace: metricNamespace,
			Name:      "connection_request_response_status_total",
			Help:      "Relay Connection Request Status",
		},
		[]string{"status"},
	)
	connectionRejectionsTotal = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Namespace: metricNamespace,
			Name:      "connection_rejections_total",
			Help:      "Relay Connection Rejected Reason",
		},
		[]string{"reason"},
	)
	connectionDurationSeconds = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Namespace: metricNamespace,
			Name:      "connection_duration_seconds",
			Help:      "Relay Connection Duration",
		},
	)

	dataTransferredBytesTotal = prometheus.NewCounter(
		prometheus.CounterOpts{
			Namespace: metricNamespace,
			Name:      "data_transferred_bytes_total",
			Help:      "Bytes Transferred Total",
		},
	)

	collectors = []prometheus.Collector{
		status,
		reservationsTotal,
		reservationRequestResponseStatusTotal,
		reservationRejectionsTotal,
		connectionsTotal,
		connectionRequestResponseStatusTotal,
		connectionRejectionsTotal,
		connectionDurationSeconds,
		dataTransferredBytesTotal,
	}
)

const (
	requestStatusOK       = "ok"
	requestStatusRejected = "rejected"
	requestStatusError    = "error"
)

// MetricsTracer is the interface for tracking metrics for relay service
type MetricsTracer interface {
	// RelayStatus tracks whether the service is currently active
	RelayStatus(enabled bool)

	// ConnectionOpened tracks metrics on opening a relay connection
	ConnectionOpened()
	// ConnectionClosed tracks metrics on closing a relay connection
	ConnectionClosed(d time.Duration)
	// ConnectionRequestHandled tracks metrics on handling a relay connection request
	ConnectionRequestHandled(status pbv2.Status)

	// ReservationAllowed tracks metrics on opening or renewing a relay reservation
	ReservationAllowed(isRenewal bool)
	// ReservationRequestClosed tracks metrics on closing a relay reservation
	ReservationClosed(cnt int)
	// ReservationRequestHandled tracks metrics on handling a relay reservation request
	ReservationRequestHandled(status pbv2.Status)

	// BytesTransferred tracks the total bytes transferred by the relay service
	BytesTransferred(cnt int)
}

type metricsTracer struct{}

var _ MetricsTracer = &metricsTracer{}

type metricsTracerSetting struct {
	reg prometheus.Registerer
}

type MetricsTracerOption func(*metricsTracerSetting)

func ( prometheus.Registerer) MetricsTracerOption {
	return func( *metricsTracerSetting) {
		if  != nil {
			.reg = 
		}
	}
}

func ( ...MetricsTracerOption) MetricsTracer {
	 := &metricsTracerSetting{reg: prometheus.DefaultRegisterer}
	for ,  := range  {
		()
	}
	metricshelper.RegisterCollectors(.reg, collectors...)
	return &metricsTracer{}
}

func ( *metricsTracer) ( bool) {
	if  {
		status.Set(1)
	} else {
		status.Set(0)
	}
}

func ( *metricsTracer) () {
	 := metricshelper.GetStringSlice()
	defer metricshelper.PutStringSlice()
	* = append(*, "opened")

	connectionsTotal.WithLabelValues(*...).Add(1)
}

func ( *metricsTracer) ( time.Duration) {
	 := metricshelper.GetStringSlice()
	defer metricshelper.PutStringSlice()
	* = append(*, "closed")

	connectionsTotal.WithLabelValues(*...).Add(1)
	connectionDurationSeconds.Observe(.Seconds())
}

func ( *metricsTracer) ( pbv2.Status) {
	 := metricshelper.GetStringSlice()
	defer metricshelper.PutStringSlice()

	 := getResponseStatus()

	* = append(*, )
	connectionRequestResponseStatusTotal.WithLabelValues(*...).Add(1)
	if  == requestStatusRejected {
		* = (*)[:0]
		* = append(*, getRejectionReason())
		connectionRejectionsTotal.WithLabelValues(*...).Add(1)
	}
}

func ( *metricsTracer) ( bool) {
	 := metricshelper.GetStringSlice()
	defer metricshelper.PutStringSlice()
	if  {
		* = append(*, "renewed")
	} else {
		* = append(*, "opened")
	}

	reservationsTotal.WithLabelValues(*...).Add(1)
}

func ( *metricsTracer) ( int) {
	 := metricshelper.GetStringSlice()
	defer metricshelper.PutStringSlice()
	* = append(*, "closed")

	reservationsTotal.WithLabelValues(*...).Add(float64())
}

func ( *metricsTracer) ( pbv2.Status) {
	 := metricshelper.GetStringSlice()
	defer metricshelper.PutStringSlice()

	 := getResponseStatus()

	* = append(*, )
	reservationRequestResponseStatusTotal.WithLabelValues(*...).Add(1)
	if  == requestStatusRejected {
		* = (*)[:0]
		* = append(*, getRejectionReason())
		reservationRejectionsTotal.WithLabelValues(*...).Add(1)
	}
}

func ( *metricsTracer) ( int) {
	dataTransferredBytesTotal.Add(float64())
}

func getResponseStatus( pbv2.Status) string {
	 := "unknown"
	switch  {
	case pbv2.Status_RESERVATION_REFUSED,
		pbv2.Status_RESOURCE_LIMIT_EXCEEDED,
		pbv2.Status_PERMISSION_DENIED,
		pbv2.Status_NO_RESERVATION,
		pbv2.Status_MALFORMED_MESSAGE:

		 = requestStatusRejected
	case pbv2.Status_UNEXPECTED_MESSAGE, pbv2.Status_CONNECTION_FAILED:
		 = requestStatusError
	case pbv2.Status_OK:
		 = requestStatusOK
	}
	return 
}

func getRejectionReason( pbv2.Status) string {
	 := "unknown"
	switch  {
	case pbv2.Status_RESERVATION_REFUSED:
		 = "ip constraint violation"
	case pbv2.Status_RESOURCE_LIMIT_EXCEEDED:
		 = "resource limit exceeded"
	case pbv2.Status_PERMISSION_DENIED:
		 = "permission denied"
	case pbv2.Status_NO_RESERVATION:
		 = "no reservation"
	case pbv2.Status_MALFORMED_MESSAGE:
		 = "malformed message"
	}
	return 
}