package basichost

import (
	

	
	ma 

	
)

const metricNamespace = "libp2p_host_addrs"

var (
	reachableAddrs = prometheus.NewGaugeVec(
		prometheus.GaugeOpts{
			Namespace: metricNamespace,
			Name:      "reachable",
			Help:      "Number of reachable addresses by transport",
		},
		[]string{"ipv", "transport"},
	)
	unreachableAddrs = prometheus.NewGaugeVec(
		prometheus.GaugeOpts{
			Namespace: metricNamespace,
			Name:      "unreachable",
			Help:      "Number of unreachable addresses by transport",
		},
		[]string{"ipv", "transport"},
	)
	unknownAddrs = prometheus.NewGaugeVec(
		prometheus.GaugeOpts{
			Namespace: metricNamespace,
			Name:      "unknown",
			Help:      "Number of addresses with unknown reachability by transport",
		},
		[]string{"ipv", "transport"},
	)
	collectors = []prometheus.Collector{
		reachableAddrs,
		unreachableAddrs,
		unknownAddrs,
	}
)

// MetricsTracker tracks autonatv2 reachability metrics
type MetricsTracker interface {
	// ConfirmedAddrsChanged updates metrics with current address reachability status
	ConfirmedAddrsChanged(reachable, unreachable, unknown []ma.Multiaddr)
	// ReachabilityTrackerClosed updated metrics on host close
	ReachabilityTrackerClosed()
}

type metricsTracker struct {
	prevReachableCounts   map[metricKey]int
	prevUnreachableCounts map[metricKey]int
	prevUnknownCounts     map[metricKey]int
	currentReachable      map[metricKey]int
	currentUnreachable    map[metricKey]int
	currentUnknown        map[metricKey]int
}

var _ MetricsTracker = &metricsTracker{}

type metricsTrackerSetting struct {
	reg prometheus.Registerer
}

type metricsTrackerOption func(*metricsTrackerSetting)

// withRegisterer sets the prometheus registerer for the metrics
func withRegisterer( prometheus.Registerer) metricsTrackerOption {
	return func( *metricsTrackerSetting) {
		if  != nil {
			.reg = 
		}
	}
}

type metricKey struct {
	ipv       string
	transport string
}

// newMetricsTracker creates a new metrics tracker for autonatv2
func newMetricsTracker( ...metricsTrackerOption) MetricsTracker {
	 := &metricsTrackerSetting{reg: prometheus.DefaultRegisterer}
	for ,  := range  {
		()
	}
	metricshelper.RegisterCollectors(.reg, collectors...)
	return &metricsTracker{
		prevReachableCounts:   make(map[metricKey]int),
		prevUnreachableCounts: make(map[metricKey]int),
		prevUnknownCounts:     make(map[metricKey]int),
		currentReachable:      make(map[metricKey]int),
		currentUnreachable:    make(map[metricKey]int),
		currentUnknown:        make(map[metricKey]int),
	}
}

func ( *metricsTracker) () {
	resetMetric(reachableAddrs, .currentReachable, .prevReachableCounts)
	resetMetric(unreachableAddrs, .currentUnreachable, .prevUnreachableCounts)
	resetMetric(unknownAddrs, .currentUnknown, .prevUnknownCounts)
}

// ConfirmedAddrsChanged updates the metrics with current address reachability counts by transport
func ( *metricsTracker) (, ,  []ma.Multiaddr) {
	updateMetric(reachableAddrs, , .currentReachable, .prevReachableCounts)
	updateMetric(unreachableAddrs, , .currentUnreachable, .prevUnreachableCounts)
	updateMetric(unknownAddrs, , .currentUnknown, .prevUnknownCounts)
}

func updateMetric( *prometheus.GaugeVec,  []ma.Multiaddr,  map[metricKey]int,  map[metricKey]int) {
	clear()
	maps.Copy(, )
	clear()
	for ,  := range  {
		 := metricshelper.GetTransport()
		 := metricshelper.GetIPVersion()
		 := metricKey{ipv: , transport: }
		[]++
	}

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

	for ,  := range  {
		* = append(*, .ipv, .transport)
		.WithLabelValues(*...).Set(float64())
		* = (*)[:0]
	}
	for  := range  {
		if ,  := [];  {
			continue
		}
		* = append(*, .ipv, .transport)
		.WithLabelValues(*...).Set(0)
		* = (*)[:0]
	}
}

func resetMetric( *prometheus.GaugeVec,  map[metricKey]int,  map[metricKey]int) {
	 := metricshelper.GetStringSlice()
	defer metricshelper.PutStringSlice()
	for  := range  {
		* = append(*, .ipv, .transport)
		.WithLabelValues(*...).Set(0)
		* = (*)[:0]
	}
	clear()
	clear()
}