package dex

import (
	"crypto/ecdsa"
	"net"
	"testing"
	"time"

	"github.com/dexon-foundation/dexon/common"
	"github.com/dexon-foundation/dexon/crypto"
	"github.com/dexon-foundation/dexon/p2p/enode"
	"github.com/dexon-foundation/dexon/p2p/enr"
)

func TestNodeTable(t *testing.T) {
	table := newNodeTable()
	ch := make(chan newRecordsEvent)
	table.SubscribeNewRecordsEvent(ch)

	records1 := []*enr.Record{
		randomNode().Record(),
		randomNode().Record(),
	}

	records2 := []*enr.Record{
		randomNode().Record(),
		randomNode().Record(),
	}

	go table.AddRecords(records1)

	select {
	case newRecords := <-ch:
		m := map[common.Hash]struct{}{}
		for _, record := range newRecords.Records {
			m[rlpHash(record)] = struct{}{}
		}

		if len(m) != len(records1) {
			t.Errorf("len mismatch: got %d, want: %d",
				len(m), len(records1))
		}

		for _, record := range records1 {
			if _, ok := m[rlpHash(record)]; !ok {
				t.Errorf("expected record (%s) not exists", rlpHash(record))
			}
		}
	case <-time.After(1 * time.Second):
		t.Error("did not receive new records event within one second")
	}

	go table.AddRecords(records2)
	select {
	case newRecords := <-ch:
		m := map[common.Hash]struct{}{}
		for _, record := range newRecords.Records {
			m[rlpHash(record)] = struct{}{}
		}

		if len(m) != len(records2) {
			t.Errorf("len mismatch: got %d, want: %d",
				len(m), len(records2))
		}

		for _, record := range records2 {
			if _, ok := m[rlpHash(record)]; !ok {
				t.Errorf("expected record (%s) not exists", rlpHash(record))
			}
		}
	case <-time.After(1 * time.Second):
		t.Error("did not receive new records event within one second")
	}

	var records []*enr.Record
	records = append(records, records1...)
	records = append(records, records2...)
	allRecords := table.Records()
	if len(allRecords) != len(records) {
		t.Errorf("all metas num mismatch: got %d, want %d",
			len(records), len(allRecords))
	}

	for _, r := range records {
		n, err := enode.New(enode.V4ID{}, r)
		if err != nil {
			t.Errorf(err.Error())
		}
		if rlpHash(r) != rlpHash(table.GetNode(n.ID()).Record()) {
			t.Errorf("record (%s) mismatch", n.ID().String())
		}
	}
}

func randomNode() *enode.Node {
	var err error
	var privkey *ecdsa.PrivateKey
	for {
		privkey, err = crypto.GenerateKey()
		if err == nil {
			break
		}
	}
	var r enr.Record
	r.Set(enr.IP(net.IP{}))
	r.Set(enr.UDP(0))
	r.Set(enr.TCP(0))
	if err := enode.SignV4(&r, privkey); err != nil {
		panic(err)
	}
	node, err := enode.New(enode.V4ID{}, &r)
	if err != nil {
		panic(err)

	}
	return node
}

func randomID() enode.ID {
	return randomNode().ID()
}