package monitor

import (
	"fmt"
	"log"
	"math"
	"math/big"
	"strconv"
	"time"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/ethclient"
)

type node struct {
	owner          common.Address
	email          string
	fined          *big.Int
	name           string
	publicKey      []byte
	nodeKeyAddress common.Address
}

func (n node) Print() {
	fmt.Println("========")
	fmt.Println(n.name)
	fmt.Println("fined: ", n.fined)
	fmt.Println("email: ", n.email)
	fmt.Println("owner: ", n.owner.Hex())
	fmt.Println("nodekey address: ", n.nodeKeyAddress.Hex())
}

// NetworkConfig represents the network config.
type NetworkConfig struct {
	WSEndpoint      string
	HTTPEndpoint    string
	EthHTTPEndpoint string
	GovAddress      common.Address
	Network         string
}

// GovAddress is governance address.
var GovAddress = common.HexToAddress("0x246FcDE58581e2754f215A523C0718C4BFc8041F")

// Fifty is 50 * 10^18
var Fifty *big.Int

// OneHundred is 100 * 10^18
var OneHundred *big.Int

func init() {
	Fifty, _ = big.NewInt(0).SetString("50000000000000000000", 10)
	OneHundred, _ = big.NewInt(0).SetString("100000000000000000000", 10)
}

// NetworkConfigMap represents the system network config mapping.
var NetworkConfigMap = map[int64]NetworkConfig{
	411: {
		WSEndpoint:      "wss://mainnet-rpc.tangerine-network.io/ws",
		HTTPEndpoint:    "https://mainnet-rpc.tangerine-network.io",
		EthHTTPEndpoint: "https://mainnet.infura.io/v3/4eb07139b29d41c59b352f21c4c9f526",
		Network:         "Mainnet",
	},
	374: {
		WSEndpoint:      "wss://testnet-rpc.tangerine-network.io/ws",
		HTTPEndpoint:    "https://testnet-rpc.tangerine-network.io",
		EthHTTPEndpoint: "https://rinkeby.infura.io/v3/4eb07139b29d41c59b352f21c4c9f526",
		Network:         "Testnet",
	},
}

// Monitor is the object to monitor tangerine network.
type Monitor struct {
	networkID            int
	notifiers            []Notifier
	backend              backendIntf
	tanBalancesCache     map[common.Address]*big.Int
	ethBalancesCache     map[common.Address]*big.Int
	ethThreshold         *big.Int // in wei
	ethThresholdString   string   // in ETH
	checkBalanceDuration time.Duration
}

// NewMonitor is the constructor for monitor object.
func NewMonitor(networkID int, backend backendIntf, threshold string) *Monitor {
	tanBalance := make(map[common.Address]*big.Int)
	ethBalance := make(map[common.Address]*big.Int)
	t, err := strconv.ParseFloat(threshold, 64)
	if err != nil {
		panic(err)
	}
	t = t * math.Pow(10, 18)
	ethThreshold := new(big.Int)
	ethThreshold.SetString(
		strconv.FormatFloat(t, 'f', 0, 64),
		10,
	)
	m := Monitor{
		networkID:            networkID,
		backend:              backend,
		tanBalancesCache:     tanBalance,
		ethBalancesCache:     ethBalance,
		checkBalanceDuration: 24 * time.Hour,
		ethThresholdString:   threshold,
		ethThreshold:         ethThreshold,
	}
	return &m
}

// Run is the entry point for running monitor.
func (m *Monitor) Run() {
	done := make(chan bool)
	finedNodeChan := make(chan node)
	go m.fetchFinedNodes(finedNodeChan)
	go m.sendNotifications(finedNodeChan, FINED)
	go m.checkTanBalance()
	go m.checkEthBalance()
	<-done
}

func (m *Monitor) checkEthBalance() {
	ethNodeChan := make(chan node)
	go m.sendNotifications(ethNodeChan, INSUFFICIENT_ETH)
	for {
		nc := NetworkConfigMap[int64(m.networkID)]
		conn, err := ethclient.Dial(nc.EthHTTPEndpoint)
		if err != nil {
			log.Println("Get Tan balance fail at")
			continue
		}
		nodes := m.backend.NodeSet()
		for i := range nodes {
			n := nodes[i]
			address := n.nodeKeyAddress
			balance := m.backend.BalanceFromAddress(conn, address)
			if balance.Cmp(m.ethThreshold) < 0 {
				if cacheBalance, exist := m.ethBalancesCache[address]; exist {
					if cacheBalance.Cmp(m.ethThreshold) >= 0 {
						ethNodeChan <- n
					}
				} else {
					ethNodeChan <- n
				}
			}
			m.ethBalancesCache[address] = balance
		}
		time.Sleep(m.checkBalanceDuration)
	}
}

func (m *Monitor) checkTanBalance() {
	tanNodeChan := make(chan node)
	go m.sendNotifications(tanNodeChan, INSUFFICIENT_TAN)
	for {
		nodes := m.backend.NodeSet()
		nc := NetworkConfigMap[int64(m.networkID)]
		conn, err := ethclient.Dial(nc.HTTPEndpoint)
		if err != nil {
			log.Println("Get TAN balance fail")
			continue
		}
		for i := range nodes {
			n := nodes[i]
			balance := m.backend.BalanceFromAddress(conn, n.nodeKeyAddress)
			if balance.Cmp(Fifty) < 0 {
				if cacheBalance, exist := m.tanBalancesCache[n.nodeKeyAddress]; exist {
					if cacheBalance.Cmp(Fifty) >= 0 {
						tanNodeChan <- n
					}
				} else {
					tanNodeChan <- n
				}
			} else if balance.Cmp(OneHundred) < 0 {
				if cacheBalance, exist := m.tanBalancesCache[n.nodeKeyAddress]; exist {
					if cacheBalance.Cmp(OneHundred) >= 0 {
						tanNodeChan <- n
					}
				} else {
					tanNodeChan <- n
				}
			}
			m.tanBalancesCache[n.nodeKeyAddress] = balance
		}
		time.Sleep(m.checkBalanceDuration)
	}
}

// Register registries the notifiers.
func (m *Monitor) Register(n Notifier) {
	m.notifiers = append(m.notifiers, n)
}

func (m *Monitor) fetchFinedNodes(nodeChan chan node) {
	m.backend.FetchFinedNodes(nodeChan)
}

func (m *Monitor) sendNotifications(nodeChan chan node, notifyType uint) {
	nc := NetworkConfigMap[int64(m.networkID)]
	for {
		node, open := <-nodeChan
		if !open {
			return
		}
		for _, notifier := range m.notifiers {
			notifier.notify(node, nc.Network,
				notifyType, m.ethThresholdString)
		}
	}
}
o/libdssialsacompat'>log</a><a href='/~lantw44/cgit/cgit.cgi/freebsd-ports-gnome/tree/audio/libdssialsacompat?id=7e7e63aa49ff223c68742601e95308bc747920a9'>tree</a><a href='/~lantw44/cgit/cgit.cgi/freebsd-ports-gnome/commit/audio/libdssialsacompat?id=7e7e63aa49ff223c68742601e95308bc747920a9'>commit</a><a href='/~lantw44/cgit/cgit.cgi/freebsd-ports-gnome/diff/audio/libdssialsacompat?id=7e7e63aa49ff223c68742601e95308bc747920a9'>diff</a><a href='/~lantw44/cgit/cgit.cgi/freebsd-ports-gnome/stats/audio/libdssialsacompat'>stats</a></td><td class='form'><form class='right' method='get' action='/~lantw44/cgit/cgit.cgi/freebsd-ports-gnome/log/audio/libdssialsacompat'>
<input type='hidden' name='id' value='7e7e63aa49ff223c68742601e95308bc747920a9'/><select name='qt'>
<option value='grep'>log msg</option>
<option value='author'>author</option>
<option value='committer'>committer</option>
<option value='range'>range</option>
</select>
<input class='txt' type='search' size='10' name='q' value=''/>
<input type='submit' value='search'/>
</form>
</td></tr></table>
<div class='path'>path: <a href='/~lantw44/cgit/cgit.cgi/freebsd-ports-gnome/log/?id=7e7e63aa49ff223c68742601e95308bc747920a9'>root</a>/<a href='/~lantw44/cgit/cgit.cgi/freebsd-ports-gnome/log/audio?id=7e7e63aa49ff223c68742601e95308bc747920a9'>audio</a>/<a href='/~lantw44/cgit/cgit.cgi/freebsd-ports-gnome/log/audio/libdssialsacompat?id=7e7e63aa49ff223c68742601e95308bc747920a9'>libdssialsacompat</a></div><div class='content'><table class='list nowrap'><tr class='nohover'><th></th><th class='left'>Commit message (<a href='/~lantw44/cgit/cgit.cgi/freebsd-ports-gnome/log/audio/libdssialsacompat?id=7e7e63aa49ff223c68742601e95308bc747920a9&amp;showmsg=1'>Expand</a>)</th><th class='left'>Author</th><th class='left'>Age</th><th class='left'>Files</th><th class='left'>Lines</th></tr>
<tr><td class='commitgraph'>* </td><td><a href='/~lantw44/cgit/cgit.cgi/freebsd-ports-gnome/commit/audio/libdssialsacompat?id=a6abc3b42030610bc8f182e713867c3b8ed9dab5'>Cleanup plist</a></td><td>Baptiste Daroussin</td><td><span title='2014-10-20 14:35:58 +0800'>2014-10-20</span></td><td>1</td><td><span class='deletions'>-3</span>/<span class='insertions'>+0</span></td></tr>
<tr><td class='commitgraph'>* </td><td><a href='/~lantw44/cgit/cgit.cgi/freebsd-ports-gnome/commit/audio/libdssialsacompat?id=fdbacb726345612e79bf62506838c230ec5709b1'>- Drop .la files, no dependees require them</a></td><td>Dmitry Marakasov</td><td><span title='2014-08-22 21:06:41 +0800'>2014-08-22</span></td><td>2</td><td><span class='deletions'>-3</span>/<span class='insertions'>+2</span></td></tr>
<tr><td class='commitgraph'>* </td><td><a href='/~lantw44/cgit/cgit.cgi/freebsd-ports-gnome/commit/audio/libdssialsacompat?id=ff44441fb3d030270aa68bce83ea210145560e1e'>- Switch to USES=libtool</a></td><td>Dmitry Marakasov</td><td><span title='2014-06-16 22:19:29 +0800'>2014-06-16</span>