package multisend

// import (
// 	"context"
// 	"fmt"
// 	"net"
// 	"net/url"
// 	"os"
// 	"os/signal"
// 	"syscall"
// 	"time"

// 	"code.wuban.net.cn/multisend/internal/logging"
// 	"github.com/ethereum/go-ethereum/ethclient"
// 	"github.com/ethereum/go-ethereum/rpc"
// )

// type ethPeerInfo struct {
// 	Addr                string
// 	Client              ethclient.Client
// 	PeerAddrs           []string
// 	SuccessfullyQueried bool
// }

// func waitForEthNetworkPeers(
// 	endpoints []string,
// 	selectionMethod string,
// 	minDiscoveredPeers int,
// 	minPeerConnectivity int,
// 	maxReturnedPeers int,
// 	timeout time.Duration,
// 	logger logging.Logger,
// ) ([]string, error) {
// 	logger.Info("waiting for eth public node to connect",
// 		"endpoints", endpoints,
// 		"selectionMethod", selectionMethod,
// 		"minDiscoveredPeers", minDiscoveredPeers,
// 		"minPeerConnectivity", minPeerConnectivity,
// 		"maxReturnedPeers", maxReturnedPeers,
// 		"timeout", fmt.Sprintf("%.2f seconds", timeout.Seconds()))

// 	cancelc := make(chan struct{}, 1)
// 	cancelTrap := trapInterrupts(func() { close(cancelc) }, logger)
// 	defer close(cancelTrap)
// 	startTime := time.Now()
// 	suppliedPeers := make(map[string]*ethPeerInfo)

// 	for _, peerURL := range endpoints {
// 		u, err := url.Parse(peerURL)
// 		if err != nil {
// 			return nil, fmt.Errorf("failed to parse peer URL %s: %s", peerURL, err)
// 		}

// 		peerIP, err := lookupFirstIPv4Addr(u.Hostname())
// 		if err != nil {
// 			return nil, fmt.Errorf("failed to resolve IP address for endpoint %s: %s", peerURL, err)
// 		}

// 		peerAddr := fmt.Sprintf("http://%s:8546", peerIP)

// 		client, err := rpc.DialWebsocket(context.Background(), peerAddr, "")
// 		if err != nil {
// 			return nil, err
// 		}

// 		suppliedPeers[peerAddr] = &ethPeerInfo{
// 			Addr:      peerAddr,
// 			Client:    client,
// 			PeerAddrs: make([]string, 0),
// 		}

// 	}

// 	peers := make(map[string]*ethPeerInfo)

// 	for a, p := range suppliedPeers {
// 		pc := *p
// 		peers[a] = &pc
// 	}

// 	for {
// 		remainingTimeout := timeout - time.Since(startTime)
// 		if remainingTimeout < 0 {
// 			return nil, fmt.Errorf("timed out waiting for Tendermint peer crawl to complete")
// 		}

// 		newPeers, err := getEthNetworkPeers(peers, remainingTimeout, cancelc, logger)
// 		if err != nil {
// 			return nil, err
// 		}
// 		// we only care if we've discovered more peers than in the previous attempt
// 		if len(newPeers) > len(peers) {
// 			peers = newPeers
// 		}
// 		peerCount := len(peers)
// 		peerConnectivity := getMinPeerConnectivity(peers)
// 		if peerCount >= minDiscoveredPeers && peerConnectivity >= minPeerConnectivity {
// 			logger.Info("All required peers connected", "count", peerCount, "minConnectivity", minPeerConnectivity)
// 			// we're done here
// 			return filterEthPeerMap(suppliedPeers, peers, selectionMethod, maxReturnedPeers), nil
// 		} else {
// 			logger.Debug(
// 				"Peers discovered so far",
// 				"count", peerCount,
// 				"minConnectivity", peerConnectivity,
// 				"remainingTimeout", timeout-time.Since(startTime),
// 			)
// 			time.Sleep(1 * time.Second)
// 		}
// 	}

// 	return nil, nil
// }

// func trapInterrupts(onKill func(), logger logging.Logger) chan struct{} {
// 	sigc := make(chan os.Signal, 1)
// 	cancelTrap := make(chan struct{})
// 	signal.Notify(sigc, os.Interrupt, syscall.SIGTERM)
// 	go func() {
// 		select {
// 		case <-sigc:
// 			logger.Info("Caught kill signal")
// 			onKill()
// 		case <-cancelTrap:
// 			logger.Debug("Interrupt trap cancelled")
// 		}
// 	}()
// 	return cancelTrap
// }

// func lookupFirstIPv4Addr(hostname string) (string, error) {
// 	ipRecords, err := net.LookupIP(hostname)
// 	if err != nil {
// 		return "", err
// 	}
// 	for _, ipRecord := range ipRecords {
// 		ipv4 := ipRecord.To4()
// 		if ipv4 != nil {
// 			return ipv4.String(), nil
// 		}
// 	}
// 	return "", fmt.Errorf("no IPv4 records for hostname: %s", hostname)
// }

// // Queries the given peers (in parallel) to construct a unique set of known
// // peers across the entire network.
// func getEthNetworkPeers(
// 	peers map[string]*ethPeerInfo, // Any existing peers we know about already
// 	timeout time.Duration, // Maximum timeout for the entire operation
// 	cancelc chan struct{}, // Allows us to cancel the polling operations
// 	logger logging.Logger,
// ) (map[string]*ethPeerInfo, error) {
// 	startTime := time.Now()
// 	peerInfoc := make(chan *ethPeerInfo, len(peers))
// 	errc := make(chan error, len(peers))
// 	logger.Debug("Querying peers for more peers", "count", len(peers), "peers", getPeerAddrs(peers))
// 	// parallelize querying all the Tendermint nodes' peers
// 	for _, peer := range peers {
// 		// reset this every time
// 		peer.SuccessfullyQueried = false

// 		go func(peer_ *ethPeerInfo) {

// 			netInfo, err := peer_.Client.NetInfo(context.Background())
// 			if err != nil {
// 				logger.Debug("Failed to query peer - skipping", "addr", peer_.Addr, "err", err)
// 				errc <- err
// 				return
// 			}
// 			peerAddrs := make([]string, 0)
// 			for _, peerInfo := range netInfo.Peers {
// 				peerAddrs = append(peerAddrs, fmt.Sprintf("http://%s:8546", peerInfo.RemoteIP))
// 			}
// 			peerInfoc <- &ethPeerInfo{
// 				Addr:                peer_.Addr,
// 				Client:              peer_.Client,
// 				PeerAddrs:           peerAddrs,
// 				SuccessfullyQueried: true,
// 			}
// 		}(peer)
// 	}
// 	result := make(map[string]*ethPeerInfo)
// 	expectedNetInfoResults := len(peers)
// 	receivedNetInfoResults := 0
// 	for {
// 		remainingTimeout := timeout - time.Since(startTime)
// 		if remainingTimeout < 0 {
// 			return nil, fmt.Errorf("timed out waiting for all peer network info to be returned")
// 		}
// 		select {
// 		case <-cancelc:
// 			return nil, fmt.Errorf("cancel signal received")
// 		case peerInfo := <-peerInfoc:
// 			result[peerInfo.Addr] = peerInfo
// 			receivedNetInfoResults++
// 		case <-errc:
// 			receivedNetInfoResults++
// 		case <-time.After(remainingTimeout):
// 			return nil, fmt.Errorf("timed out while waiting for all peer network info to be returned")
// 		}
// 		if receivedNetInfoResults >= expectedNetInfoResults {
// 			return resolveTendermintPeerMap(result), nil
// 		} else {
// 			// wait a little before polling  again
// 			time.Sleep(1 * time.Second)
// 		}
// 	}
// }

// func resolveTendermintPeerMap(peers map[string]*ethPeerInfo) map[string]*ethPeerInfo {
// 	result := make(map[string]*ethPeerInfo)
// 	for addr, peer := range peers {
// 		result[addr] = peer

// 		for _, peerAddr := range peer.PeerAddrs {

// 			client, err := rpc.DialWebsocket(context.Background(), peerAddr, "")
// 			if err != nil {
// 				return nil
// 			}
// 			if _, exists := result[peerAddr]; !exists {
// 				result[peerAddr] = &ethPeerInfo{
// 					Addr:      peerAddr,
// 					Client:    client,
// 					PeerAddrs: make([]string, 0),
// 				}
// 			}
// 		}
// 	}
// 	return result
// }

// func getPeerAddrs(peers map[string]*ethPeerInfo) []string {
// 	results := make([]string, 0)
// 	for _, peer := range peers {
// 		results = append(results, peer.Addr)
// 	}
// 	return results
// }

// func getMinPeerConnectivity(peers map[string]*ethPeerInfo) int {
// 	minPeers := len(peers)
// 	for _, peer := range peers {
// 		// we only care about peers we've successfully queried so far
// 		if !peer.SuccessfullyQueried {
// 			continue
// 		}
// 		peerCount := len(peer.PeerAddrs)
// 		if peerCount > 0 && peerCount < minPeers {
// 			minPeers = peerCount
// 		}
// 	}
// 	return minPeers
// }

// func filterEthPeerMap(suppliedPeers, newPeers map[string]*ethPeerInfo, selectionMethod string, maxCount int) []string {
// 	result := make([]string, 0)
// 	for peerAddr := range newPeers {
// 		u, err := url.Parse(peerAddr)
// 		if err != nil {
// 			continue
// 		}
// 		addr := fmt.Sprintf("ws://%s:8546", u.Hostname())
// 		switch selectionMethod {
// 		case SelectSuppliedEndpoints:
// 			// only add it to the result if it was in the original list
// 			if _, ok := suppliedPeers[peerAddr]; ok {
// 				result = append(result, addr)
// 			}
// 		case SelectDiscoveredEndpoints:
// 			// only add it to the result if it wasn't in the original list
// 			if _, ok := suppliedPeers[peerAddr]; !ok {
// 				result = append(result, addr)
// 			}
// 		default:
// 			// otherwise, always add it
// 			result = append(result, addr)
// 		}
// 		if len(result) >= maxCount {
// 			break
// 		}
// 	}
// 	return result
// }
