package core

import (
	"fmt"

	"github.com/pkg/errors"

	"github.com/tendermint/tendermint/p2p"
	ctypes "github.com/tendermint/tendermint/rpc/core/types"
	rpctypes "github.com/tendermint/tendermint/rpc/lib/types"
)

// Get network info.
//
// ```shell
// curl 'localhost:26657/net_info'
// ```
//
// ```go
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
// err := client.Start()
// if err != nil {
//   // handle error
// }
// defer client.Stop()
// info, err := client.NetInfo()
// ```
//
// > The above command returns JSON structured like this:
//
// ```json
// {
//   "jsonrpc": "2.0",
//   "id": "",
//   "result": {
//   	"listening": true,
//   	"listeners": [
//   		"Listener(@)"
//   	],
//   	"n_peers": "3",
//   	"peers": [
//   		{
//   			"node_info": {
//   				"protocol_version": {
//   					"p2p": "7",
//   					"block": "8",
//   					"app": "1"
//   				},
//   				"id": "93529da3435c090d02251a050342b6a488d4ab56",
//   				"listen_addr": "tcp://0.0.0.0:26656",
//   				"network": "chain-RFo6qC",
//   				"version": "0.30.0",
//   				"channels": "4020212223303800",
//   				"moniker": "fc89e4ed23f2",
//   				"other": {
//   					"tx_index": "on",
//   					"rpc_address": "tcp://0.0.0.0:26657"
//   				}
//   			},
//   			"is_outbound": true,
//   			"connection_status": {
//   				"Duration": "3475230558",
//   				"SendMonitor": {
//   					"Active": true,
//   					"Start": "2019-02-14T12:40:47.52Z",
//   					"Duration": "3480000000",
//   					"Idle": "240000000",
//   					"Bytes": "4512",
//   					"Samples": "9",
//   					"InstRate": "1338",
//   					"CurRate": "2046",
//   					"AvgRate": "1297",
//   					"PeakRate": "6570",
//   					"BytesRem": "0",
//   					"TimeRem": "0",
//   					"Progress": 0
//   				},
//   				"RecvMonitor": {
//   					"Active": true,
//   					"Start": "2019-02-14T12:40:47.52Z",
//   					"Duration": "3480000000",
//   					"Idle": "280000000",
//   					"Bytes": "4489",
//   					"Samples": "10",
//   					"InstRate": "1821",
//   					"CurRate": "1663",
//   					"AvgRate": "1290",
//   					"PeakRate": "5512",
//   					"BytesRem": "0",
//   					"TimeRem": "0",
//   					"Progress": 0
//   				},
//   				"Channels": [
//   					{
//   						"ID": 48,
//   						"SendQueueCapacity": "1",
//   						"SendQueueSize": "0",
//   						"Priority": "5",
//   						"RecentlySent": "0"
//   					},
//   					{
//   						"ID": 64,
//   						"SendQueueCapacity": "1000",
//   						"SendQueueSize": "0",
//   						"Priority": "10",
//   						"RecentlySent": "14"
//   					},
//   					{
//   						"ID": 32,
//   						"SendQueueCapacity": "100",
//   						"SendQueueSize": "0",
//   						"Priority": "5",
//   						"RecentlySent": "619"
//   					},
//   					{
//   						"ID": 33,
//   						"SendQueueCapacity": "100",
//   						"SendQueueSize": "0",
//   						"Priority": "10",
//   						"RecentlySent": "1363"
//   					},
//   					{
//   						"ID": 34,
//   						"SendQueueCapacity": "100",
//   						"SendQueueSize": "0",
//   						"Priority": "5",
//   						"RecentlySent": "2145"
//   					},
//   					{
//   						"ID": 35,
//   						"SendQueueCapacity": "2",
//   						"SendQueueSize": "0",
//   						"Priority": "1",
//   						"RecentlySent": "0"
//   					},
//   					{
//   						"ID": 56,
//   						"SendQueueCapacity": "1",
//   						"SendQueueSize": "0",
//   						"Priority": "5",
//   						"RecentlySent": "0"
//   					},
//   					{
//   						"ID": 0,
//   						"SendQueueCapacity": "10",
//   						"SendQueueSize": "0",
//   						"Priority": "1",
//   						"RecentlySent": "10"
//   					}
//   				]
//   			},
//   			"remote_ip": "192.167.10.3"
//   		},
//      ...
//   }
// ```
func NetInfo(ctx *rpctypes.Context) (*ctypes.ResultNetInfo, error) {
	out, in, _ := p2pPeers.NumPeers()
	peers := make([]ctypes.Peer, 0, out+in)
	for _, peer := range p2pPeers.Peers().List() {
		nodeInfo, ok := peer.NodeInfo().(p2p.DefaultNodeInfo)
		if !ok {
			return nil, fmt.Errorf("peer.NodeInfo() is not DefaultNodeInfo")
		}
		peers = append(peers, ctypes.Peer{
			NodeInfo:         nodeInfo,
			IsOutbound:       peer.IsOutbound(),
			ConnectionStatus: peer.Status(),
			RemoteIP:         peer.RemoteIP().String(),
		})
	}
	// TODO: Should we include PersistentPeers and Seeds in here?
	// PRO: useful info
	// CON: privacy
	return &ctypes.ResultNetInfo{
		Listening: p2pTransport.IsListening(),
		Listeners: p2pTransport.Listeners(),
		NPeers:    len(peers),
		Peers:     peers,
	}, nil
}

func UnsafeDialSeeds(ctx *rpctypes.Context, seeds []string) (*ctypes.ResultDialSeeds, error) {
	if len(seeds) == 0 {
		return &ctypes.ResultDialSeeds{}, errors.New("No seeds provided")
	}
	// starts go routines to dial each peer after random delays
	logger.Info("DialSeeds", "addrBook", addrBook, "seeds", seeds)
	err := p2pPeers.DialPeersAsync(addrBook, seeds, false)
	if err != nil {
		return &ctypes.ResultDialSeeds{}, err
	}
	return &ctypes.ResultDialSeeds{Log: "Dialing seeds in progress. See /net_info for details"}, nil
}

func UnsafeDialPeers(ctx *rpctypes.Context, peers []string, persistent bool) (*ctypes.ResultDialPeers, error) {
	if len(peers) == 0 {
		return &ctypes.ResultDialPeers{}, errors.New("No peers provided")
	}
	// starts go routines to dial each peer after random delays
	logger.Info("DialPeers", "addrBook", addrBook, "peers", peers, "persistent", persistent)
	err := p2pPeers.DialPeersAsync(addrBook, peers, persistent)
	if err != nil {
		return &ctypes.ResultDialPeers{}, err
	}
	return &ctypes.ResultDialPeers{Log: "Dialing peers in progress. See /net_info for details"}, nil
}

// Get genesis file.
//
// ```shell
// curl 'localhost:26657/genesis'
// ```
//
// ```go
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
// err := client.Start()
// if err != nil {
//   // handle error
// }
// defer client.Stop()
// genesis, err := client.Genesis()
// ```
//
// > The above command returns JSON structured like this:
//
// ```json
// {
// 	"error": "",
// 	"result": {
// 		"genesis": {
// 			"app_hash": "",
// 			"validators": [
// 				{
// 					"name": "",
// 					"power": "10",
// 					"pub_key": {
// 						"data": "68DFDA7E50F82946E7E8546BED37944A422CD1B831E70DF66BA3B8430593944D",
// 						"type": "ed25519"
// 					}
// 				}
// 			],
// 			"chain_id": "test-chain-6UTNIN",
// 			"genesis_time": "2017-05-29T15:05:41.671Z"
// 		}
// 	},
// 	"id": "",
// 	"jsonrpc": "2.0"
// }
// ```
func Genesis(ctx *rpctypes.Context) (*ctypes.ResultGenesis, error) {
	return &ctypes.ResultGenesis{Genesis: genDoc}, nil
}
