package blankhost

import (
	
	
	
	

	
	
	
	
	
	
	
	
	

	logging 

	ma 
	mstream 
)

var log = logging.Logger("blankhost")

// BlankHost is the thinnest implementation of the host.Host interface
type BlankHost struct {
	n        network.Network
	mux      *mstream.MultistreamMuxer[protocol.ID]
	cmgr     connmgr.ConnManager
	eventbus event.Bus
	emitters struct {
		evtLocalProtocolsUpdated event.Emitter
	}
}

type config struct {
	cmgr     connmgr.ConnManager
	eventBus event.Bus
}

type Option = func(cfg *config)

func ( connmgr.ConnManager) Option {
	return func( *config) {
		.cmgr = 
	}
}

func ( event.Bus) Option {
	return func( *config) {
		.eventBus = 
	}
}

func ( network.Network,  ...Option) *BlankHost {
	 := config{
		cmgr: &connmgr.NullConnMgr{},
	}
	for ,  := range  {
		(&)
	}

	 := &BlankHost{
		n:        ,
		cmgr:     .cmgr,
		mux:      mstream.NewMultistreamMuxer[protocol.ID](),
		eventbus: .eventBus,
	}
	if .eventbus == nil {
		.eventbus = eventbus.NewBus(eventbus.WithMetricsTracer(eventbus.NewMetricsTracer()))
	}

	// subscribe the connection manager to network notifications (has no effect with NullConnMgr)
	.Notify(.cmgr.Notifee())

	var  error
	if .emitters.evtLocalProtocolsUpdated,  = .eventbus.Emitter(&event.EvtLocalProtocolsUpdated{});  != nil {
		return nil
	}

	.SetStreamHandler(.newStreamHandler)

	// persist a signed peer record for self to the peerstore.
	if  := .initSignedRecord();  != nil {
		log.Errorf("error creating blank host, err=%s", )
		return nil
	}

	return 
}

func ( *BlankHost) () error {
	,  := peerstore.GetCertifiedAddrBook(.n.Peerstore())
	if ! {
		log.Error("peerstore does not support signed records")
		return errors.New("peerstore does not support signed records")
	}
	 := peer.PeerRecordFromAddrInfo(peer.AddrInfo{ID: .ID(), Addrs: .Addrs()})
	,  := record.Seal(, .Peerstore().PrivKey(.ID()))
	if  != nil {
		log.Errorf("failed to create signed record for self, err=%s", )
		return fmt.Errorf("failed to create signed record for self, err=%s", )
	}
	_,  = .ConsumePeerRecord(, peerstore.PermanentAddrTTL)
	if  != nil {
		log.Errorf("failed to persist signed record to peerstore,err=%s", )
		return fmt.Errorf("failed to persist signed record for self, err=%s", )
	}
	return 
}

var _ host.Host = (*BlankHost)(nil)

func ( *BlankHost) () []ma.Multiaddr {
	,  := .n.InterfaceListenAddresses()
	if  != nil {
		log.Debug("error retrieving network interface addrs: ", )
		return nil
	}

	return 
}

func ( *BlankHost) () error {
	return .n.Close()
}

func ( *BlankHost) ( context.Context,  peer.AddrInfo) error {
	// absorb addresses into peerstore
	.Peerstore().AddAddrs(.ID, .Addrs, peerstore.TempAddrTTL)

	 := .n.ConnsToPeer(.ID)
	if len() > 0 {
		return nil
	}

	,  := .Network().DialPeer(, .ID)
	if  != nil {
		return fmt.Errorf("failed to dial: %w", )
	}
	return 
}

func ( *BlankHost) () peerstore.Peerstore {
	return .n.Peerstore()
}

func ( *BlankHost) () peer.ID {
	return .n.LocalPeer()
}

func ( *BlankHost) ( context.Context,  peer.ID,  ...protocol.ID) (network.Stream, error) {
	,  := .n.NewStream(, )
	if  != nil {
		return nil, fmt.Errorf("failed to open stream: %w", )
	}

	,  := mstream.SelectOneOf(, )
	if  != nil {
		.Reset()
		return nil, fmt.Errorf("failed to negotiate protocol: %w", )
	}

	.SetProtocol()
	.Peerstore().AddProtocols(, )

	return , nil
}

func ( *BlankHost) ( protocol.ID) {
	.Mux().RemoveHandler()
	.emitters.evtLocalProtocolsUpdated.Emit(event.EvtLocalProtocolsUpdated{
		Removed: []protocol.ID{},
	})
}

func ( *BlankHost) ( protocol.ID,  network.StreamHandler) {
	.Mux().AddHandler(, func( protocol.ID,  io.ReadWriteCloser) error {
		 := .(network.Stream)
		.SetProtocol()
		()
		return nil
	})
	.emitters.evtLocalProtocolsUpdated.Emit(event.EvtLocalProtocolsUpdated{
		Added: []protocol.ID{},
	})
}

func ( *BlankHost) ( protocol.ID,  func(protocol.ID) bool,  network.StreamHandler) {
	.Mux().AddHandlerWithFunc(, , func( protocol.ID,  io.ReadWriteCloser) error {
		 := .(network.Stream)
		.SetProtocol()
		()
		return nil
	})
	.emitters.evtLocalProtocolsUpdated.Emit(event.EvtLocalProtocolsUpdated{
		Added: []protocol.ID{},
	})
}

// newStreamHandler is the remote-opened stream handler for network.Network
func ( *BlankHost) ( network.Stream) {
	, ,  := .Mux().Negotiate()
	if  != nil {
		log.Infow("protocol negotiation failed", "error", )
		.Reset()
		return
	}

	.SetProtocol()

	(, )
}

// TODO: i'm not sure this really needs to be here
func ( *BlankHost) () protocol.Switch {
	return .mux
}

// TODO: also not sure this fits... Might be better ways around this (leaky abstractions)
func ( *BlankHost) () network.Network {
	return .n
}

func ( *BlankHost) () connmgr.ConnManager {
	return .cmgr
}

func ( *BlankHost) () event.Bus {
	return .eventbus
}