package iroh

import (
	
	
	
	

	
	
	

	amhelp 
	am 
	arpc 
)

const ALPN = "arpc/1"

type ServerOpts struct {
	arpc.ServerOpts
	SecretKey      *key.SecretKey
	AllowedPubKeys []key.PublicKey
	OptsIroh       []iroh.Option
}

type ClientOpts struct {
	arpc.ClientOpts
	Key      key.SecretKey
	OptsIroh []iroh.Option
}

type MuxOpts struct {
	arpc.MuxOpts
	SecretKey      *key.SecretKey
	AllowedPubKeys []key.PublicKey
	OptsIroh       []iroh.Option
}

// NewServer is [arpc.NewServer] decorated with a go-iroh overlay.
func (
	 context.Context,  string,  string,  am.Api,
	 *ServerOpts,
) (*arpc.Server, *iroh.Endpoint, error) {
	//

	if  == nil {
		 = &ServerOpts{}
	}
	if  == "" {
		return nil, nil, fmt.Errorf("addr required for iroh server")
	}

	 := []iroh.Option{}
	 := iroh.WithBindAddr(netip.MustParseAddrPort())
	 = append(, )

	if .SecretKey != nil {
		 = append(, iroh.WithSecretKey(*.SecretKey))
	}
	 = append(, .OptsIroh...)

	,  := iroh.Bind(, ...)
	if  != nil {
		return nil, nil, 
	}

	 = .SetALPNs([]string{ALPN})
	if  != nil {
		_ = .Shutdown()
		return nil, nil, 
	}

	,  := .ListenStreams()
	if  != nil {
		_ = .Shutdown()
		return nil, nil, 
	}

	,  := arpc.NewServer(, "", , , &.ServerOpts)
	if  != nil {
		.Close()
		_ = .Shutdown()
		return nil, nil, 
	}

	// bind iroh-specific cleanup
	amhelp.DisposeBind(.Mach, func( string,  context.Context) {
		_ = .Shutdown()
	})

	var  net.Listener = &irohListener{
		StreamListener: ,
		allowedPubKeys: .AllowedPubKeys,
	}
	.Listener.Store(&)

	return , , nil
}

// NewClient is [arpc.NewClient] decorated with a go-iroh overlay.
func (
	 context.Context,  string,  string,  am.Schema,
	 *ClientOpts,
) (*arpc.Client, *iroh.Endpoint, error) {
	//

	if  == nil {
		 = &ClientOpts{}
	}
	if  == "" {
		return nil, nil, fmt.Errorf("irohAddr required for iroh client")
	}

	,  := endpointticket.Decode()
	if  != nil {
		return nil, nil, 
	}

	 := []iroh.Option{}
	if !.Key.IsZero() {
		 = append(, iroh.WithSecretKey(.Key))
	}
	 = append(, .OptsIroh...)

	,  := iroh.Bind(, ...)
	if  != nil {
		return nil, nil, 
	}

	,  := arpc.NewClient(, "", , , &.ClientOpts)
	if  != nil {
		_ = .Shutdown()
		return nil, nil, 
	}

	,  := .Connect(, , ALPN)
	if  != nil {
		_ = .Shutdown()
		return nil, nil, 
	}

	,  := .OpenStreamConn()
	if  != nil {
		_ = .Shutdown()
		return nil, nil, 
	}

	// Write a dummy byte to force the stream to open on the server side
	if ,  := .Write([]byte{0});  != nil {
		.Close()
		_ = .Shutdown()
		return nil, nil, 
	}

	.Conn.Store(&)

	return , , nil
}

// NewMux is [arpc.NewMux] decorated with a go-iroh overlay.
func (
	 context.Context,  string,  string,  am.Api,
	 *MuxOpts,
) (*arpc.Mux, *iroh.Endpoint, error) {
	//

	if  == nil {
		 = &MuxOpts{}
	}
	if  == "" {
		return nil, nil, fmt.Errorf("addr required for iroh mux")
	}

	 := []iroh.Option{}
	 := iroh.WithBindAddr(netip.MustParseAddrPort())
	 = append(, )

	if .SecretKey != nil {
		 = append(, iroh.WithSecretKey(*.SecretKey))
	}
	 = append(, .OptsIroh...)

	,  := iroh.Bind(, ...)
	if  != nil {
		return nil, nil, 
	}

	 = .SetALPNs([]string{ALPN})
	if  != nil {
		_ = .Shutdown()
		return nil, nil, 
	}

	,  := .ListenStreams()
	if  != nil {
		_ = .Shutdown()
		return nil, nil, 
	}

	,  := arpc.NewMux(, "", , , &.MuxOpts)
	if  != nil {
		.Close()
		_ = .Shutdown()
		return nil, nil, 
	}

	.Listener = &irohListener{
		StreamListener: ,
		allowedPubKeys: .AllowedPubKeys,
	}

	return , , nil
}

type irohListener struct {
	*iroh.StreamListener
	allowedPubKeys []key.PublicKey
}

func ( *irohListener) () (net.Conn, error) {
	for {
		,  := .StreamListener.Accept()
		if  != nil {
			return nil, 
		}

		if len(.allowedPubKeys) > 0 {
			var  key.EndpointID
			 := false
			if ,  := .(interface{ () key.EndpointID });  {
				 = .()
				for ,  := range .allowedPubKeys {
					if key.EndpointID() ==  {
						 = true
						break
					}
				}
			}
			if ! {
				.Close()
				continue
			}
		}

		// Read the dummy byte
		 := make([]byte, 1)
		if ,  := .Read();  != nil {
			.Close()
			return nil, 
		}
		return , nil
	}
}