package peer

import (
	"net/netip"
	"sort"
	"sync"
	"time"
)

type pubAddrStore struct {
	lock      sync.Mutex
	localPub  bool
	localAddr netip.AddrPort
	lastSeen  map[netip.AddrPort]time.Time
	addrList  []netip.AddrPort
}

func newPubAddrStore(localAddr netip.AddrPort) *pubAddrStore {
	return &pubAddrStore{
		localPub:  localAddr.IsValid(),
		localAddr: localAddr,
		lastSeen:  map[netip.AddrPort]time.Time{},
		addrList:  make([]netip.AddrPort, 0, 32),
	}
}

func (store *pubAddrStore) Store(addr netip.AddrPort) {
	if store.localPub {
		return
	}

	if !addr.IsValid() {
		return
	}

	if addr.Addr().IsPrivate() {
		return
	}

	store.lock.Lock()
	defer store.lock.Unlock()

	if _, exists := store.lastSeen[addr]; !exists {
		store.addrList = append(store.addrList, addr)
	}
	store.lastSeen[addr] = time.Now()
	store.sort()
}

func (store *pubAddrStore) Get() (addrs [8]netip.AddrPort) {
	store.lock.Lock()
	defer store.lock.Unlock()

	store.clean()

	if store.localPub {
		addrs[0] = store.localAddr
		return
	}

	copy(addrs[:], store.addrList)
	return
}

func (store *pubAddrStore) clean() {
	if store.localPub {
		return
	}

	for ip, lastSeen := range store.lastSeen {
		if time.Since(lastSeen) > timeoutInterval {
			delete(store.lastSeen, ip)
		}
	}
	store.addrList = store.addrList[:0]
	for ip := range store.lastSeen {
		store.addrList = append(store.addrList, ip)
	}
	store.sort()
}

func (store *pubAddrStore) sort() {
	sort.Slice(store.addrList, func(i, j int) bool {
		return store.lastSeen[store.addrList[j]].Before(store.lastSeen[store.addrList[i]])
	})
}
