package libp2pquic

import (
	
	
	

	ic 
	
	
	tpt 
	p2ptls 
	
	ma 
	
)

// A listener listens for QUIC connections.
type listener struct {
	reuseListener   quicreuse.Listener
	transport       *transport
	rcmgr           network.ResourceManager
	privKey         ic.PrivKey
	localPeer       peer.ID
	localMultiaddrs map[quic.Version]ma.Multiaddr
}

func newListener( quicreuse.Listener,  *transport,  peer.ID,  ic.PrivKey,  network.ResourceManager) (listener, error) {
	 := make(map[quic.Version]ma.Multiaddr)
	for ,  := range .Multiaddrs() {
		if ,  := .ValueForProtocol(ma.P_QUIC_V1);  == nil {
			[quic.Version1] = 
		}
	}

	return listener{
		reuseListener:   ,
		transport:       ,
		rcmgr:           ,
		privKey:         ,
		localPeer:       ,
		localMultiaddrs: ,
	}, nil
}

// Accept accepts new connections.
func ( *listener) () (tpt.CapableConn, error) {
	for {
		,  := .reuseListener.Accept(context.Background())
		if  != nil {
			return nil, 
		}
		,  := .wrapConn()
		if  != nil {
			log.Debug("failed to setup connection", "err", )
			.CloseWithError(quic.ApplicationErrorCode(network.ConnResourceLimitExceeded), "")
			continue
		}
		.transport.addConn(, )
		if .transport.gater != nil && !(.transport.gater.InterceptAccept() && .transport.gater.InterceptSecured(network.DirInbound, .remotePeerID, )) {
			.closeWithError(quic.ApplicationErrorCode(network.ConnGated), "connection gated")
			continue
		}

		// return through active hole punching if any
		 := holePunchKey{addr: .RemoteAddr().String(), peer: .remotePeerID}
		var  bool
		.transport.holePunchingMx.Lock()
		,  := .transport.holePunching[]
		if  && !.fulfilled {
			.connCh <- 
			 = true
			.fulfilled = true
		}
		.transport.holePunchingMx.Unlock()
		if  {
			continue
		}
		return , nil
	}
}

// wrapConn wraps a QUIC connection into a libp2p [tpt.CapableConn].
// If wrapping fails. The caller is responsible for cleaning up the
// connection.
func ( *listener) ( *quic.Conn) (*conn, error) {
	,  := quicreuse.ToQuicMultiaddr(.RemoteAddr(), .ConnectionState().Version)
	if  != nil {
		return nil, 
	}
	,  := network.UnwrapConnManagementScope(.Context())
	if  != nil {
		 = nil
		// Don't error here.
		// Setup scope if we don't have scope from quicreuse.
		// This is better than failing so that users that don't use quicreuse.ConnContext option with the resource
		// manager work correctly.
	}
	if  == nil {
		,  = .rcmgr.OpenConnection(network.DirInbound, false, )
		if  != nil {
			log.Debug("resource manager blocked incoming connection", "addr", .RemoteAddr(), "err", )
			return nil, 
		}
	}
	,  := .wrapConnWithScope(, , )
	if  != nil {
		.Done()
		return nil, 
	}

	return , nil
}

func ( *listener) ( *quic.Conn,  network.ConnManagementScope,  ma.Multiaddr) (*conn, error) {
	// The tls.Config used to establish this connection already verified the certificate chain.
	// Since we don't have any way of knowing which tls.Config was used though,
	// we have to re-determine the peer's identity here.
	// Therefore, this is expected to never fail.
	,  := p2ptls.PubKeyFromCertChain(.ConnectionState().TLS.PeerCertificates)
	if  != nil {
		return nil, 
	}
	,  := peer.IDFromPublicKey()
	if  != nil {
		return nil, 
	}
	if  := .SetPeer();  != nil {
		log.Debug("resource manager blocked incoming connection for peer", "peer", , "addr", .RemoteAddr(), "err", )
		return nil, 
	}

	,  := .localMultiaddrs[.ConnectionState().Version]
	if ! {
		return nil, errors.New("unknown QUIC version:" + .ConnectionState().Version.String())
	}

	return &conn{
		quicConn:        ,
		transport:       .transport,
		scope:           ,
		localPeer:       .localPeer,
		localMultiaddr:  ,
		remoteMultiaddr: ,
		remotePeerID:    ,
		remotePubKey:    ,
	}, nil
}

// Close closes the listener.
func ( *listener) () error {
	return .reuseListener.Close()
}

// Addr returns the address of this listener.
func ( *listener) () net.Addr {
	return .reuseListener.Addr()
}