package iroh

import (
	

	
	
	
)

// PathCandidate is one path offered to a [PathSelector].
type PathCandidate struct {
	// Addr is the path's transport address.
	Addr netaddr.TransportAddr
	// RTT is the smoothed round-trip time observed on the path.
	RTT time.Duration
}

// PathSelector chooses the preferred path among candidates for a remote
// endpoint. Implementations must not block.
//
// Returning ok=false keeps the current selection unchanged. A nil current means
// no path is currently selected.
type PathSelector interface {
	Select(current netaddr.TransportAddr, candidates []PathCandidate) (selected netaddr.TransportAddr, ok bool)
}

// BiasedRttPathSelector is the default [PathSelector]. It sorts paths by
// (tier, biased RTT): direct IP and custom paths beat relay paths, and within a
// tier the lowest biased RTT wins. IPv6 paths receive a 3ms RTT advantage.
// Switching within a tier requires the candidate's biased RTT to be at least
// 5ms better than the current path.
//
// The zero value is ready to use.
type BiasedRttPathSelector struct{}

var _ PathSelector = BiasedRttPathSelector{}

// Select implements [PathSelector].
func (BiasedRttPathSelector) ( netaddr.TransportAddr,  []PathCandidate) (netaddr.TransportAddr, bool) {
	 := make([]socket.PathCandidate, 0, len())
	for ,  := range  {
		,  := socketAddrFromTransportAddr(.Addr)
		if ! {
			continue
		}
		 = append(, socket.PathCandidate{Addr: , RTT: .RTT})
	}
	var  *socket.Addr
	if  != nil {
		if ,  := socketAddrFromTransportAddr();  {
			 = &
		}
	}
	,  := socket.BiasedRttPathSelector{}.Select(, )
	if ! {
		return nil, false
	}
	,  := transportAddrFromSocket()
	if  == nil {
		return nil, false
	}
	return , true
}

type pathSelectorAdapter struct {
	selector PathSelector
}

func ( pathSelectorAdapter) ( *socket.Addr,  []socket.PathCandidate) (socket.Addr, bool) {
	 := make([]PathCandidate, 0, len())
	 := make([]socket.Addr, 0, len())
	for ,  := range  {
		,  := transportAddrFromSocket(.Addr)
		if  == nil {
			continue
		}
		 = append(, PathCandidate{Addr: , RTT: .RTT})
		 = append(, .Addr)
	}
	var  netaddr.TransportAddr
	if  != nil {
		, _ = transportAddrFromSocket(*)
	}
	,  := .selector.Select(, )
	if ! ||  == nil {
		return socket.Addr{}, false
	}
	for ,  := range  {
		if .Addr.Compare() == 0 {
			return [], true
		}
	}
	return socket.Addr{}, false
}

func socketAddrFromTransportAddr( netaddr.TransportAddr) (socket.Addr, bool) {
	switch a := .(type) {
	case netaddr.IPAddr:
		return socket.IPAddr(.Addr), true
	case netaddr.RelayAddr:
		// The endpoint id is irrelevant for public selection policy; endpoint
		// adapters map selections back to the original socket candidates.
		return socket.RelayAddr(.URL, key.EndpointID{}), true
	case netaddr.CustomAddr:
		return socket.CustomAddr(), true
	default:
		return socket.Addr{}, false
	}
}