package upgrader

import (
	
	
	
	
	

	
	
	
	ipnet 
	
	
	
	

	manet 
	mss 
)

// ErrNilPeer is returned when attempting to upgrade an outbound connection
// without specifying a peer ID.
var ErrNilPeer = errors.New("nil peer")

// AcceptQueueLength is the number of connections to fully setup before not accepting any new connections
var AcceptQueueLength = 16

const (
	defaultAcceptTimeout    = 15 * time.Second
	defaultNegotiateTimeout = 60 * time.Second
)

type Option func(*upgrader) error

func ( time.Duration) Option {
	return func( *upgrader) error {
		.acceptTimeout = 
		return nil
	}
}

type StreamMuxer struct {
	ID    protocol.ID
	Muxer network.Multiplexer
}

// Upgrader is a multistream upgrader that can upgrade an underlying connection
// to a full transport connection (secure and multiplexed).
type upgrader struct {
	psk       ipnet.PSK
	connGater connmgr.ConnectionGater
	rcmgr     network.ResourceManager

	muxerMuxer *mss.MultistreamMuxer[protocol.ID]
	muxers     []StreamMuxer
	muxerIDs   []protocol.ID

	security      []sec.SecureTransport
	securityMuxer *mss.MultistreamMuxer[protocol.ID]
	securityIDs   []protocol.ID

	// AcceptTimeout is the maximum duration an Accept is allowed to take.
	// This includes the time between accepting the raw network connection,
	// protocol selection as well as the handshake, if applicable.
	//
	// If unset, the default value (15s) is used.
	acceptTimeout time.Duration
}

var _ transport.Upgrader = &upgrader{}

func ( []sec.SecureTransport,  []StreamMuxer,  ipnet.PSK,  network.ResourceManager,  connmgr.ConnectionGater,  ...Option) (transport.Upgrader, error) {
	 := &upgrader{
		acceptTimeout: defaultAcceptTimeout,
		rcmgr:         ,
		connGater:     ,
		psk:           ,
		muxerMuxer:    mss.NewMultistreamMuxer[protocol.ID](),
		muxers:        ,
		security:      ,
		securityMuxer: mss.NewMultistreamMuxer[protocol.ID](),
	}
	for ,  := range  {
		if  := ();  != nil {
			return nil, 
		}
	}
	if .rcmgr == nil {
		.rcmgr = &network.NullResourceManager{}
	}
	.muxerIDs = make([]protocol.ID, 0, len())
	for ,  := range  {
		.muxerMuxer.AddHandler(.ID, nil)
		.muxerIDs = append(.muxerIDs, .ID)
	}
	.securityIDs = make([]protocol.ID, 0, len())
	for ,  := range  {
		.securityMuxer.AddHandler(.ID(), nil)
		.securityIDs = append(.securityIDs, .ID())
	}
	return , nil
}

// UpgradeListener upgrades the passed multiaddr-net listener into a full libp2p-transport listener.
func ( *upgrader) ( transport.Transport,  manet.Listener) transport.Listener {
	return .UpgradeGatedMaListener(, .GateMaListener())
}

func ( *upgrader) ( manet.Listener) transport.GatedMaListener {
	return &gatedMaListener{
		Listener:  ,
		rcmgr:     .rcmgr,
		connGater: .connGater,
	}
}

// UpgradeGatedMaListener upgrades the passed multiaddr-net listener into a full libp2p-transport listener.
func ( *upgrader) ( transport.Transport,  transport.GatedMaListener) transport.Listener {
	,  := context.WithCancel(context.Background())
	 := &listener{
		GatedMaListener: ,
		upgrader:        ,
		transport:       ,
		rcmgr:           .rcmgr,
		threshold:       newThreshold(AcceptQueueLength),
		incoming:        make(chan transport.CapableConn),
		cancel:          ,
		ctx:             ,
	}
	go .handleIncoming()
	return 
}

// Upgrade upgrades the multiaddr/net connection into a full libp2p-transport connection.
func ( *upgrader) ( context.Context,  transport.Transport,  manet.Conn,  network.Direction,  peer.ID,  network.ConnManagementScope) (transport.CapableConn, error) {
	,  := .upgrade(, , , , , )
	if  != nil {
		.Done()
		return nil, 
	}
	return , nil
}

func ( *upgrader) ( context.Context,  transport.Transport,  manet.Conn,  network.Direction,  peer.ID,  network.ConnManagementScope) (transport.CapableConn, error) {
	if  == network.DirOutbound &&  == "" {
		return nil, ErrNilPeer
	}
	var  network.ConnStats
	if ,  := .(network.ConnStat);  {
		 = .Stat()
	}

	var  net.Conn = 
	if .psk != nil {
		,  := pnet.NewProtectedConn(.psk, )
		if  != nil {
			.Close()
			return nil, fmt.Errorf("failed to setup private network protector: %w", )
		}
		 = 
	} else if ipnet.ForcePrivateNetwork {
		log.Error("tried to dial with no Private Network Protector but usage of Private Networks is forced by the environment")
		return nil, ipnet.ErrNotInPrivateNetwork
	}

	 :=  == network.DirInbound
	, ,  := .setupSecurity(, , , )
	if  != nil {
		.Close()
		return nil, fmt.Errorf("failed to negotiate security protocol: %w", )
	}

	// call the connection gater, if one is registered.
	if .connGater != nil && !.connGater.InterceptSecured(, .RemotePeer(), ) {
		if  := .Close();  != nil {
			log.Errorw("failed to close connection", "peer", , "addr", .RemoteMultiaddr(), "error", )
		}
		return nil, fmt.Errorf("gater rejected connection with peer %s and addr %s with direction %d",
			.RemotePeer(), .RemoteMultiaddr(), )
	}
	// Only call SetPeer if it hasn't already been set -- this can happen when we don't know
	// the peer in advance and in some bug scenarios.
	if .PeerScope() == nil {
		if  := .SetPeer(.RemotePeer());  != nil {
			log.Debugw("resource manager blocked connection for peer", "peer", .RemotePeer(), "addr", .RemoteAddr(), "error", )
			if  := .Close();  != nil {
				log.Errorw("failed to close connection", "peer", , "addr", .RemoteMultiaddr(), "error", )
			}
			return nil, fmt.Errorf("resource manager connection with peer %s and addr %s with direction %d",
				.RemotePeer(), .RemoteMultiaddr(), )
		}
	}

	, ,  := .setupMuxer(, , , .PeerScope())
	if  != nil {
		.Close()
		return nil, fmt.Errorf("failed to negotiate stream multiplexer: %w", )
	}

	 := &transportConn{
		MuxedConn:                 ,
		ConnMultiaddrs:            ,
		ConnSecurity:              ,
		transport:                 ,
		stat:                      ,
		scope:                     ,
		muxer:                     ,
		security:                  ,
		usedEarlyMuxerNegotiation: .ConnState().UsedEarlyMuxerNegotiation,
	}
	return , nil
}

func ( *upgrader) ( context.Context,  net.Conn,  peer.ID,  bool) (sec.SecureConn, protocol.ID, error) {
	,  := .negotiateSecurity(, , )
	if  != nil {
		return nil, "", 
	}
	if  {
		,  := .SecureInbound(, , )
		return , .ID(), 
	}
	,  := .SecureOutbound(, , )
	return , .ID(), 
}

func ( *upgrader) ( net.Conn,  bool) (*StreamMuxer, error) {
	if  := .SetDeadline(time.Now().Add(defaultNegotiateTimeout));  != nil {
		return nil, 
	}

	var  protocol.ID
	if  {
		, ,  := .muxerMuxer.Negotiate()
		if  != nil {
			return nil, 
		}
		 = 
	} else {
		,  := mss.SelectOneOf(.muxerIDs, )
		if  != nil {
			return nil, 
		}
		 = 
	}

	if  := .SetDeadline(time.Time{});  != nil {
		return nil, 
	}

	if  := .getMuxerByID();  != nil {
		return , nil
	}
	return nil, fmt.Errorf("selected protocol we don't have a transport for")
}

func ( *upgrader) ( protocol.ID) *StreamMuxer {
	for ,  := range .muxers {
		if .ID ==  {
			return &
		}
	}
	return nil
}

func ( *upgrader) ( context.Context,  sec.SecureConn,  bool,  network.PeerScope) (protocol.ID, network.MuxedConn, error) {
	 := .ConnState().StreamMultiplexer
	// Use muxer selected from security handshake if available. Otherwise fall back to multistream-selection.
	if len() > 0 {
		 := .getMuxerByID()
		if  == nil {
			return "", nil, fmt.Errorf("selected a muxer we don't know: %s", )
		}
		,  := .Muxer.NewConn(, , )
		if  != nil {
			return "", nil, 
		}
		return , , nil
	}

	type  struct {
		  network.MuxedConn
		 protocol.ID
		     error
	}

	 := make(chan , 1)
	// TODO: The muxer should take a context.
	go func() {
		,  := .negotiateMuxer(, )
		if  != nil {
			 <- {: }
			return
		}
		,  := .Muxer.NewConn(, , )
		 <- {: , : .ID, : }
	}()

	select {
	case  := <-:
		return ., ., .
	case <-.Done():
		// interrupt this process
		.Close()
		// wait to finish
		<-
		return "", nil, .Err()
	}
}

func ( *upgrader) ( protocol.ID) sec.SecureTransport {
	for ,  := range .security {
		if .ID() ==  {
			return 
		}
	}
	return nil
}

func ( *upgrader) ( context.Context,  net.Conn,  bool) (sec.SecureTransport, error) {
	type  struct {
		 protocol.ID
		   error
	}

	 := make(chan , 1)
	go func() {
		if  {
			var  
			., _, . = .securityMuxer.Negotiate()
			 <- 
			return
		}
		var  
		., . = mss.SelectOneOf(.securityIDs, )
		 <- 
	}()

	select {
	case  := <-:
		if . != nil {
			return nil, .
		}
		if  := .getSecurityByID(.);  != nil {
			return , nil
		}
		return nil, fmt.Errorf("selected unknown security transport: %s", .)
	case <-.Done():
		// We *must* do this. We have outstanding work on the connection, and it's no longer safe to use.
		.Close()
		<- // wait to stop using the connection.
		return nil, .Err()
	}
}