package iroh

import (
	
	
	
	

	
	
	
)

// MemoryProvenance is the default provenance string for [MemoryLookup] items.
const MemoryProvenance = "memory_lookup"

// MemoryLookup is an in-memory [AddressResolver] for addressing information added
// out-of-band, such as from an endpoint ticket. Applications add and remove
// entries; resolution returns the stored info for an id.
//
// The zero value is not usable; create one with [NewMemoryLookup] or
// [NewMemoryLookupWithProvenance]. A MemoryLookup is safe for concurrent use.
//
// It is the Go analog of iroh's MemoryLookup.
type MemoryLookup struct {
	mu         sync.RWMutex
	endpoints  map[key.EndpointID]storedInfo
	provenance string
}

type storedInfo struct {
	data        dns.EndpointData
	lastUpdated time.Time
}

// NewMemoryLookup returns an empty MemoryLookup using [MemoryProvenance].
func () *MemoryLookup {
	return NewMemoryLookupWithProvenance(MemoryProvenance)
}

// NewMemoryLookupWithProvenance returns an empty MemoryLookup whose resolved
// [Item]s report the given provenance.
func ( string) *MemoryLookup {
	return &MemoryLookup{
		endpoints:  make(map[key.EndpointID]storedInfo),
		provenance: ,
	}
}

// MemoryLookupFromInfo returns a MemoryLookup pre-populated with infos.
func ( ...dns.EndpointInfo) *MemoryLookup {
	 := NewMemoryLookup()
	for ,  := range  {
		.AddEndpointInfo()
	}
	return 
}

// SetEndpointInfo replaces all stored info for info.ID, returning the previous
// [dns.EndpointData] and whether an entry existed.
func ( *MemoryLookup) ( dns.EndpointInfo) (dns.EndpointData, bool) {
	.mu.Lock()
	defer .mu.Unlock()
	,  := .endpoints[.ID]
	.endpoints[.ID] = storedInfo{data: .Data, lastUpdated: time.Now()}
	return .data, 
}

// AddEndpointInfo merges info into the stored entry for info.ID: new direct
// addresses are appended and the user data is overwritten. If no entry exists,
// one is created.
func ( *MemoryLookup) ( dns.EndpointInfo) {
	.mu.Lock()
	defer .mu.Unlock()
	,  := .endpoints[.ID]
	if ! {
		.endpoints[.ID] = storedInfo{data: .Data, lastUpdated: time.Now()}
		return
	}
	.data.AddAddrs(.Data.Addrs()...)
	.data.SetUserData(.Data.UserData())
	.lastUpdated = time.Now()
	.endpoints[.ID] = 
}

// AddEndpointAddr is a convenience wrapper for [MemoryLookup.AddEndpointInfo]
// taking an [netaddr.EndpointAddr].
func ( *MemoryLookup) ( netaddr.EndpointAddr) {
	.AddEndpointInfo(dns.EndpointInfoFromAddr())
}

// GetEndpointInfo returns the stored info for id and whether it exists.
func ( *MemoryLookup) ( key.EndpointID) (dns.EndpointInfo, bool) {
	.mu.RLock()
	defer .mu.RUnlock()
	,  := .endpoints[]
	if ! {
		return dns.EndpointInfo{}, false
	}
	return dns.EndpointInfo{ID: , Data: .data}, true
}

// RemoveEndpointInfo removes and returns the info for id, and whether it
// existed.
func ( *MemoryLookup) ( key.EndpointID) (dns.EndpointInfo, bool) {
	.mu.Lock()
	defer .mu.Unlock()
	,  := .endpoints[]
	if ! {
		return dns.EndpointInfo{}, false
	}
	delete(.endpoints, )
	return dns.EndpointInfo{ID: , Data: .data}, true
}

// Resolve returns the stored info for id, or nil if there is no entry.
func ( *MemoryLookup) ( context.Context,  key.EndpointID) iter.Seq2[Item, error] {
	.mu.RLock()
	,  := .endpoints[]
	.mu.RUnlock()
	if ! {
		return nil
	}
	 := uint64(.lastUpdated.UnixMicro())
	 := NewItem(dns.EndpointInfo{ID: , Data: .data}, .provenance, &)
	return func( func(Item, error) bool) {
		if .Err() == nil {
			(, nil)
		}
	}
}

// FilteredAddressPublisher wraps an [AddressPublisher], applying an
// [AddrFilter] to the data before publishing it to the inner service.
//
// The zero value is not usable; create one with [NewFilteredAddressPublisher].
type FilteredAddressPublisher struct {
	inner  AddressPublisher
	filter AddrFilter
}

// NewFilteredAddressPublisher wraps inner so that published data is filtered by
// f before reaching inner.
func ( AddressPublisher,  AddrFilter) FilteredAddressPublisher {
	return FilteredAddressPublisher{inner: , filter: }
}

// Inner returns the wrapped publisher.
func ( FilteredAddressPublisher) () AddressPublisher { return .inner }

// Publish filters data and publishes it to the inner service.
func ( FilteredAddressPublisher) ( dns.EndpointData) {
	.inner.Publish(applyFilter(, .filter))
}