package socket

import (
	

	
	
	
	
)

// RelayTransport is the magic socket's relay path: it owns a [RelayActor],
// forwards datagrams received from relays into the [MagicConn]'s recv channel
// (tagged so they surface as a [RelayMappedAddr]), and routes outgoing datagrams
// addressed to a relay mapped address to the right relay connection.
//
// It is the Go analog of the Rust RelayTransport
// (iroh/src/socket/transports/relay.rs:31). Create one with [NewRelayTransport]
// and start it with [RelayTransport.Serve]. The zero value is not usable.
type RelayTransport struct {
	sock   *Socket
	actor  *RelayActor
	recvCh chan<- recvBatch
}

// NewRelayTransport returns a RelayTransport that drives actor and delivers
// received relay datagrams to recvCh. sock supplies the relay mapped-address
// table shared with the [MagicConn]. The transport does not start the actor;
// call [RelayTransport.Serve].
func ( *Socket,  *RelayActor,  chan<- recvBatch) *RelayTransport {
	return &RelayTransport{sock: , actor: , recvCh: }
}

// Serve runs the relay actor and the recv-forwarding loop until ctx is
// cancelled. It blocks; run it in its own goroutine.
func ( *RelayTransport) ( context.Context) {
	go .actor.Run()
	.forwardRecv()
}

// forwardRecv drains datagrams from the actor and forwards each as a recvBatch
// tagged with the relay [Addr], so [MagicConn.recvAddr] rewrites it to the
// relay mapped IPv6 ULA quic-go addresses the path by. A relay transmit may
// carry a GRO batch (segment size set); each segment is delivered as its own
// recvBatch, matching the re-batching in the Rust poll_recv
// (iroh/src/socket/transports/relay.rs:115).
func ( *RelayTransport) ( context.Context) {
	for {
		select {
		case <-.Done():
			return
		case ,  := <-.actor.Recv():
			if ! {
				return
			}
			.deliver(, )
		}
	}
}

// deliver splits dm into single datagrams (by its segment size) and forwards
// each to the recv channel.
func ( *RelayTransport) ( context.Context,  RelayRecvDatagram) {
	 := RelayAddr(.URL, .Src)
	for ,  := range splitSegments(.Datagrams) {
		 := make([]byte, len())
		copy(, )
		select {
		case .recvCh <- recvBatch{data: , info: RecvInfo{Remote: }}:
		case <-.Done():
			return
		}
	}
}

// splitSegments returns the individual datagrams in d. A zero segment size means
// a single datagram; otherwise the contents are sliced into segment-size strides
// (the final segment may be shorter), matching the relay GRO stride
// (iroh/src/socket/transports/relay.rs:154).
func splitSegments( relayproto.Datagrams) [][]byte {
	if .SegmentSize == 0 || len(.Contents) == 0 {
		return [][]byte{.Contents}
	}
	 := int(.SegmentSize)
	var  [][]byte
	for  := 0;  < len(.Contents);  +=  {
		 :=  + 
		if  > len(.Contents) {
			 = len(.Contents)
		}
		 = append(, .Contents[:])
	}
	return 
}

// Send routes p to the relay addressed by the relay mapped address m. It looks
// up the (relay url, endpoint id) pair m maps to and queues a datagram to the
// relay actor. It reports whether the datagram was routed; a false result means
// the address is unknown or the send queue was full, in which case the datagram
// is treated as lost (QUIC's loss recovery retransmits), matching the Rust
// blackhole-on-failure invariant (iroh/src/socket/transports.rs:1176).
func ( *RelayTransport) ( RelayMappedAddr,  []byte) bool {
	,  := .sock.LookupRelay()
	if ! {
		return false
	}
	return .actor.Send(RelaySendItem{
		RemoteEndpoint: .EID,
		URL:            .URL,
		Datagrams:      relayproto.DatagramsFromBytes(),
	})
}

// SetHomeRelay designates url as the endpoint's home relay. See
// [RelayActor.SetHomeRelay].
func ( *RelayTransport) ( netaddr.RelayURL) { .actor.SetHomeRelay() }

// InsertRelay adds or replaces a relay config in the underlying actor.
func ( *RelayTransport) ( netaddr.RelayURL,  relay.Config) (relay.Config, bool) {
	return .actor.InsertRelay(, )
}

// RemoveRelay removes a relay config from the underlying actor.
func ( *RelayTransport) ( netaddr.RelayURL) (relay.Config, bool) {
	return .actor.RemoveRelay()
}

// HomeRelayStatus returns a watcher over the home relay's connection status. See
// [RelayActor.HomeRelayStatus].
func ( *RelayTransport) () watch.Observer[*RelayStatus] {
	return .actor.HomeRelayStatus()
}