//go:build !windows && !riscv64 && !loong64

package tcp

import (
	
	
	

	
	
	
	
	manet 
	
)

var (
	newConns      *prometheus.CounterVec
	closedConns   *prometheus.CounterVec
	segsSentDesc  *prometheus.Desc
	segsRcvdDesc  *prometheus.Desc
	bytesSentDesc *prometheus.Desc
	bytesRcvdDesc *prometheus.Desc
)

const collectFrequency = 10 * time.Second

var defaultCollector *aggregatingCollector

var initMetricsOnce sync.Once

func initMetrics() {
	segsSentDesc = prometheus.NewDesc("tcp_sent_segments_total", "TCP segments sent", nil, nil)
	segsRcvdDesc = prometheus.NewDesc("tcp_rcvd_segments_total", "TCP segments received", nil, nil)
	bytesSentDesc = prometheus.NewDesc("tcp_sent_bytes", "TCP bytes sent", nil, nil)
	bytesRcvdDesc = prometheus.NewDesc("tcp_rcvd_bytes", "TCP bytes received", nil, nil)

	defaultCollector = newAggregatingCollector()
	prometheus.MustRegister(defaultCollector)

	const  = "direction"

	newConns = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Name: "tcp_connections_new_total",
			Help: "TCP new connections",
		},
		[]string{},
	)
	prometheus.MustRegister(newConns)
	closedConns = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Name: "tcp_connections_closed_total",
			Help: "TCP connections closed",
		},
		[]string{},
	)
	prometheus.MustRegister(closedConns)
}

type aggregatingCollector struct {
	cronOnce sync.Once

	mutex                sync.Mutex
	highestID            uint64
	conns                map[uint64] /* id */ *tracingConn
	rtts                 prometheus.Histogram
	connDurations        prometheus.Histogram
	segsSent, segsRcvd   uint64
	bytesSent, bytesRcvd uint64
}

var _ prometheus.Collector = &aggregatingCollector{}

func newAggregatingCollector() *aggregatingCollector {
	 := &aggregatingCollector{
		conns: make(map[uint64]*tracingConn),
		rtts: prometheus.NewHistogram(prometheus.HistogramOpts{
			Name:    "tcp_rtt",
			Help:    "TCP round trip time",
			Buckets: prometheus.ExponentialBuckets(0.001, 1.25, 40), // 1ms to ~6000ms
		}),
		connDurations: prometheus.NewHistogram(prometheus.HistogramOpts{
			Name:    "tcp_connection_duration",
			Help:    "TCP Connection Duration",
			Buckets: prometheus.ExponentialBuckets(1, 1.5, 40), // 1s to ~12 weeks
		}),
	}
	return 
}

func ( *aggregatingCollector) ( *tracingConn) uint64 {
	.mutex.Lock()
	defer .mutex.Unlock()
	.highestID++
	.conns[.highestID] = 
	return .highestID
}

func ( *aggregatingCollector) ( uint64) {
	delete(.conns, )
}

func ( *aggregatingCollector) ( chan<- *prometheus.Desc) {
	 <- .rtts.Desc()
	 <- .connDurations.Desc()
	if hasSegmentCounter {
		 <- segsSentDesc
		 <- segsRcvdDesc
	}
	if hasByteCounter {
		 <- bytesSentDesc
		 <- bytesRcvdDesc
	}
}

func ( *aggregatingCollector) () {
	 := time.NewTicker(collectFrequency)
	defer .Stop()

	for  := range .C {
		.gatherMetrics()
	}
}

func ( *aggregatingCollector) ( time.Time) {
	.mutex.Lock()
	defer .mutex.Unlock()

	.segsSent = 0
	.segsRcvd = 0
	.bytesSent = 0
	.bytesRcvd = 0
	for ,  := range .conns {
		,  := .getTCPInfo()
		if  != nil {
			if strings.Contains(.Error(), "use of closed network connection") {
				continue
			}
			log.Errorf("Failed to get TCP info: %s", )
			continue
		}
		if hasSegmentCounter {
			.segsSent += getSegmentsSent()
			.segsRcvd += getSegmentsRcvd()
		}
		if hasByteCounter {
			.bytesSent += getBytesSent()
			.bytesRcvd += getBytesRcvd()
		}
		.rtts.Observe(.RTT.Seconds())
		.connDurations.Observe(.Sub(.startTime).Seconds())
	}
}

func ( *aggregatingCollector) ( chan<- prometheus.Metric) {
	// Start collecting the metrics collection the first time Collect is called.
	.cronOnce.Do(func() {
		.gatherMetrics(time.Now())
		go .cron()
	})

	.mutex.Lock()
	defer .mutex.Unlock()

	 <- .rtts
	 <- .connDurations
	if hasSegmentCounter {
		,  := prometheus.NewConstMetric(segsSentDesc, prometheus.CounterValue, float64(.segsSent))
		if  != nil {
			log.Errorf("creating tcp_sent_segments_total metric failed: %v", )
			return
		}
		,  := prometheus.NewConstMetric(segsRcvdDesc, prometheus.CounterValue, float64(.segsRcvd))
		if  != nil {
			log.Errorf("creating tcp_rcvd_segments_total metric failed: %v", )
			return
		}
		 <- 
		 <- 
	}
	if hasByteCounter {
		,  := prometheus.NewConstMetric(bytesSentDesc, prometheus.CounterValue, float64(.bytesSent))
		if  != nil {
			log.Errorf("creating tcp_sent_bytes metric failed: %v", )
			return
		}
		,  := prometheus.NewConstMetric(bytesRcvdDesc, prometheus.CounterValue, float64(.bytesRcvd))
		if  != nil {
			log.Errorf("creating tcp_rcvd_bytes metric failed: %v", )
			return
		}
		 <- 
		 <- 
	}
}

func ( *aggregatingCollector) ( *tracingConn,  string) {
	.mutex.Lock()
	.removeConn(.id)
	.mutex.Unlock()
	closedConns.WithLabelValues().Inc()
}

type tracingConn struct {
	id uint64

	collector *aggregatingCollector

	startTime time.Time
	isClient  bool

	manet.Conn
	tcpConn   *tcp.Conn
	closeOnce sync.Once
	closeErr  error
}

// newTracingConn wraps a manet.Conn with a tracingConn. A nil collector will use the default collector.
func newTracingConn( manet.Conn,  *aggregatingCollector,  bool) (*tracingConn, error) {
	initMetricsOnce.Do(func() { initMetrics() })
	,  := tcp.NewConn()
	if  != nil {
		return nil, 
	}
	 := &tracingConn{
		startTime: time.Now(),
		isClient:  ,
		Conn:      ,
		tcpConn:   ,
		collector: ,
	}
	if .collector == nil {
		.collector = defaultCollector
	}
	.id = .collector.AddConn()
	newConns.WithLabelValues(.getDirection()).Inc()
	return , nil
}

func ( *tracingConn) () string {
	if .isClient {
		return "outgoing"
	}
	return "incoming"
}

func ( *tracingConn) () error {
	.closeOnce.Do(func() {
		.collector.ClosedConn(, .getDirection())
		.closeErr = .Conn.Close()
	})
	return .closeErr
}

func ( *tracingConn) () (*tcpinfo.Info, error) {
	var  tcpinfo.Info
	var  [256]byte
	,  := .tcpConn.Option(.Level(), .Name(), [:])
	if  != nil {
		return nil, 
	}
	 := .(*tcpinfo.Info)
	return , nil
}

type tracingListener struct {
	transport.GatedMaListener
	collector *aggregatingCollector
}

// newTracingListener wraps a manet.Listener with a tracingListener. A nil collector will use the default collector.
func newTracingListener( transport.GatedMaListener,  *aggregatingCollector) *tracingListener {
	return &tracingListener{GatedMaListener: , collector: }
}

func ( *tracingListener) () (manet.Conn, network.ConnManagementScope, error) {
	, ,  := .GatedMaListener.Accept()
	if  != nil {
		if  != nil {
			.Done()
			log.Errorf("BUG: got non-nil scope but also an error: %s", )
		}
		return nil, nil, 
	}

	,  := newTracingConn(, .collector, false)
	if  != nil {
		log.Errorf("failed to create tracingConn from %T: %s", , )
		.Close()
		.Done()
		return nil, nil, 
	}
	return , , nil
}