package iroh

import (
	
	
	

	
	
)

// DNSProvenance is the provenance string for [DNSAddressLookup] items.
const DNSProvenance = "dns"

// dnsStaggerMs are the delays, in milliseconds, after which additional DNS
// lookups are issued while earlier ones are still in flight. Each query has its
// own 3s timeout, so a lookup aborts after at most 6s.
//
// iroh/src/address_lookup/dns.rs DNS_STAGGERING_MS.
var dnsStaggerMs = []int{200, 300, 600, 1000, 2000, 3000}

// DNSAddressLookup resolves endpoint addressing information from DNS. It queries
// TXT records under "_iroh.<z32-endpoint-id>.<origin>" using the endpoint's DNS
// resolver, where <origin> is the discovery origin domain.
//
// The zero value is not usable; create one with [NewDNSAddressLookup] or
// [N0DNSAddressLookup].
//
// It is the Go analog of iroh's DNSAddressLookup.
type DNSAddressLookup struct {
	origin   string
	resolver *dns.Resolver
}

// NewDNSAddressLookup returns a DNSAddressLookup querying origin (for example
// [dns.N0DNSEndpointOriginProd]) using resolver. If resolver is nil, a default
// [dns.Resolver] backed by the system DNS configuration is used.
func ( string,  *dns.Resolver) *DNSAddressLookup {
	if  == nil {
		 = &dns.Resolver{}
	}
	return &DNSAddressLookup{origin: , resolver: }
}

// N0DNSAddressLookup returns a DNSAddressLookup using the number0 production
// discovery origin ([dns.N0DNSEndpointOriginProd]).
func ( *dns.Resolver) *DNSAddressLookup {
	return NewDNSAddressLookup(dns.N0DNSEndpointOriginProd, )
}

// Resolve looks up id in DNS, issuing staggered concurrent queries and yielding
// the first successful result or an error.
func ( *DNSAddressLookup) ( context.Context,  key.EndpointID) iter.Seq2[Item, error] {
	return func( func(Item, error) bool) {
		,  := .lookupStaggered(, )
		if  != nil {
			if .Err() == nil {
				(Item{}, lookupErr(DNSProvenance, ))
			}
			return
		}
		if .Err() == nil {
			(NewItem(, DNSProvenance, nil), nil)
		}
	}
}

// lookupStaggered issues a first DNS lookup immediately and additional ones
// after each delay in [dnsStaggerMs] while earlier attempts are still in
// flight, returning the first success or the last error once all attempts fail.
func ( *DNSAddressLookup) ( context.Context,  key.EndpointID) (dns.EndpointInfo, error) {
	,  := context.WithCancel()
	defer ()

	type  struct {
		 dns.EndpointInfo
		  error
	}
	 := len(dnsStaggerMs) + 1
	 := make(chan , )

	 := func() {
		go func() {
			,  := .resolver.LookupEndpointByID(, , .origin)
			 <- {: , : }
		}()
	}

	()
	 := make([]*time.Timer, len(dnsStaggerMs))
	for ,  := range dnsStaggerMs {
		[] = time.AfterFunc(time.Duration()*time.Millisecond, )
	}
	defer func() {
		for ,  := range  {
			.Stop()
		}
	}()

	var  error
	for  := 0;  < ; ++ {
		select {
		case  := <-:
			if . == nil {
				return ., nil
			}
			 = .
		case <-.Done():
			return dns.EndpointInfo{}, .Err()
		}
	}
	return dns.EndpointInfo{}, 
}