package swarm

import (
	
	
	
	
	

	
	
	

	ma 

	
)

const metricNamespace = "libp2p_swarm"

var (
	connsOpened = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Namespace: metricNamespace,
			Name:      "connections_opened_total",
			Help:      "Connections Opened",
		},
		[]string{"dir", "transport", "security", "muxer", "early_muxer", "ip_version"},
	)
	keyTypes = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Namespace: metricNamespace,
			Name:      "key_types_total",
			Help:      "key type",
		},
		[]string{"dir", "key_type"},
	)
	connsClosed = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Namespace: metricNamespace,
			Name:      "connections_closed_total",
			Help:      "Connections Closed",
		},
		[]string{"dir", "transport", "security", "muxer", "early_muxer", "ip_version"},
	)
	dialError = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Namespace: metricNamespace,
			Name:      "dial_errors_total",
			Help:      "Dial Error",
		},
		[]string{"transport", "error", "ip_version"},
	)
	connDuration = prometheus.NewHistogramVec(
		prometheus.HistogramOpts{
			Namespace: metricNamespace,
			Name:      "connection_duration_seconds",
			Help:      "Duration of a Connection",
			Buckets:   prometheus.ExponentialBuckets(1.0/16, 2, 25), // up to 24 days
		},
		[]string{"dir", "transport", "security", "muxer", "early_muxer", "ip_version"},
	)
	connHandshakeLatency = prometheus.NewHistogramVec(
		prometheus.HistogramOpts{
			Namespace: metricNamespace,
			Name:      "handshake_latency_seconds",
			Help:      "Duration of the libp2p Handshake",
			Buckets:   prometheus.ExponentialBuckets(0.001, 1.3, 35),
		},
		[]string{"transport", "security", "muxer", "early_muxer", "ip_version"},
	)
	dialsPerPeer = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Namespace: metricNamespace,
			Name:      "dials_per_peer_total",
			Help:      "Number of addresses dialed per peer",
		},
		[]string{"outcome", "num_dials"},
	)
	dialLatency = prometheus.NewHistogramVec(
		prometheus.HistogramOpts{
			Namespace: metricNamespace,
			Name:      "dial_latency_seconds",
			Help:      "time taken to establish connection with the peer",
			Buckets:   []float64{0.001, 0.01, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.75, 1, 2},
		},
		[]string{"outcome", "num_dials"},
	)
	dialRankingDelay = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Namespace: metricNamespace,
			Name:      "dial_ranking_delay_seconds",
			Help:      "delay introduced by the dial ranking logic",
			Buckets:   []float64{0.001, 0.01, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.75, 1, 2},
		},
	)
	blackHoleSuccessCounterState = prometheus.NewGaugeVec(
		prometheus.GaugeOpts{
			Namespace: metricNamespace,
			Name:      "black_hole_filter_state",
			Help:      "State of the black hole filter",
		},
		[]string{"name"},
	)
	blackHoleSuccessCounterSuccessFraction = prometheus.NewGaugeVec(
		prometheus.GaugeOpts{
			Namespace: metricNamespace,
			Name:      "black_hole_filter_success_fraction",
			Help:      "Fraction of successful dials among the last n requests",
		},
		[]string{"name"},
	)
	blackHoleSuccessCounterNextRequestAllowedAfter = prometheus.NewGaugeVec(
		prometheus.GaugeOpts{
			Namespace: metricNamespace,
			Name:      "black_hole_filter_next_request_allowed_after",
			Help:      "Number of requests after which the next request will be allowed",
		},
		[]string{"name"},
	)
	collectors = []prometheus.Collector{
		connsOpened,
		keyTypes,
		connsClosed,
		dialError,
		connDuration,
		connHandshakeLatency,
		dialsPerPeer,
		dialRankingDelay,
		dialLatency,
		blackHoleSuccessCounterSuccessFraction,
		blackHoleSuccessCounterState,
		blackHoleSuccessCounterNextRequestAllowedAfter,
	}
)

type MetricsTracer interface {
	OpenedConnection(network.Direction, crypto.PubKey, network.ConnectionState, ma.Multiaddr)
	ClosedConnection(network.Direction, time.Duration, network.ConnectionState, ma.Multiaddr)
	CompletedHandshake(time.Duration, network.ConnectionState, ma.Multiaddr)
	FailedDialing(ma.Multiaddr, error, error)
	DialCompleted(success bool, totalDials int, latency time.Duration)
	DialRankingDelay(d time.Duration)
	UpdatedBlackHoleSuccessCounter(name string, state BlackHoleState, nextProbeAfter int, successFraction float64)
}

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 appendConnectionState( []string,  network.ConnectionState) []string {
	if .Transport == "" {
		// This shouldn't happen, unless the transport doesn't properly set the Transport field in the ConnectionState.
		 = append(, "unknown")
	} else {
		 = append(, .Transport)
	}
	// These might be empty, depending on the transport.
	// For example, QUIC doesn't set security nor muxer.
	 = append(, string(.Security))
	 = append(, string(.StreamMultiplexer))

	 := "false"
	if .UsedEarlyMuxerNegotiation {
		 = "true"
	}
	 = append(, )
	return 
}

func ( *metricsTracer) ( network.Direction,  crypto.PubKey,  network.ConnectionState,  ma.Multiaddr) {
	 := metricshelper.GetStringSlice()
	defer metricshelper.PutStringSlice()

	* = append(*, metricshelper.GetDirection())
	* = appendConnectionState(*, )
	* = append(*, metricshelper.GetIPVersion())
	connsOpened.WithLabelValues(*...).Inc()

	* = (*)[:0]
	* = append(*, metricshelper.GetDirection())
	* = append(*, .Type().String())
	keyTypes.WithLabelValues(*...).Inc()
}

func ( *metricsTracer) ( network.Direction,  time.Duration,  network.ConnectionState,  ma.Multiaddr) {
	 := metricshelper.GetStringSlice()
	defer metricshelper.PutStringSlice()

	* = append(*, metricshelper.GetDirection())
	* = appendConnectionState(*, )
	* = append(*, metricshelper.GetIPVersion())
	connsClosed.WithLabelValues(*...).Inc()
	connDuration.WithLabelValues(*...).Observe(.Seconds())
}

func ( *metricsTracer) ( time.Duration,  network.ConnectionState,  ma.Multiaddr) {
	 := metricshelper.GetStringSlice()
	defer metricshelper.PutStringSlice()

	* = appendConnectionState(*, )
	* = append(*, metricshelper.GetIPVersion())
	connHandshakeLatency.WithLabelValues(*...).Observe(.Seconds())
}

func ( *metricsTracer) ( ma.Multiaddr,  error,  error) {
	 := metricshelper.GetTransport()
	 := "other"
	// dial deadline exceeded or the the parent contexts deadline exceeded
	if errors.Is(, context.DeadlineExceeded) || errors.Is(, context.DeadlineExceeded) {
		 = "deadline"
	} else if errors.Is(, context.Canceled) {
		// dial was cancelled.
		if errors.Is(, context.Canceled) {
			// parent context was canceled
			 = "application canceled"
		} else if errors.Is(, errConcurrentDialSuccessful) {
			 = "canceled: concurrent dial successful"
		} else {
			// something else
			 = "canceled: other"
		}
	} else {
		,  := .(net.Error)
		if  && .Timeout() {
			 = "timeout"
		} else if strings.Contains(.Error(), "connect: connection refused") {
			 = "connection refused"
		}
	}

	 := metricshelper.GetStringSlice()
	defer metricshelper.PutStringSlice()

	* = append(*, , )
	* = append(*, metricshelper.GetIPVersion())
	dialError.WithLabelValues(*...).Inc()
}

func ( *metricsTracer) ( bool,  int,  time.Duration) {
	 := metricshelper.GetStringSlice()
	defer metricshelper.PutStringSlice()
	if  {
		* = append(*, "success")
	} else {
		* = append(*, "failed")
	}

	 := [...]string{"0", "1", "2", "3", "4", "5", ">=6"}
	var  string
	if  < len() {
		 = []
	} else {
		 = [len()-1]
	}
	* = append(*, )
	dialsPerPeer.WithLabelValues(*...).Inc()
	dialLatency.WithLabelValues(*...).Observe(.Seconds())
}

func ( *metricsTracer) ( time.Duration) {
	dialRankingDelay.Observe(.Seconds())
}

func ( *metricsTracer) ( string,  BlackHoleState,
	 int,  float64) {
	 := metricshelper.GetStringSlice()
	defer metricshelper.PutStringSlice()

	* = append(*, )

	blackHoleSuccessCounterState.WithLabelValues(*...).Set(float64())
	blackHoleSuccessCounterSuccessFraction.WithLabelValues(*...).Set()
	blackHoleSuccessCounterNextRequestAllowedAfter.WithLabelValues(*...).Set(float64())
}