package address

import (
	"bytes"
	"encoding/base32"
	"fmt"
	"math"
	"math/rand"
	"strconv"
	"testing"
	"time"

	ffi "github.com/filecoin-project/filecoin-ffi"
	"github.com/multiformats/go-varint"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/filecoin-project/lotus/lib/crypto"
)

func init() {
	rand.Seed(time.Now().Unix())
}

func TestRandomIDAddress(t *testing.T) {
	assert := assert.New(t)

	addr, err := NewIDAddress(uint64(rand.Int()))
	assert.NoError(err)
	assert.Equal(ID, addr.Protocol())

	str, err := encode(Testnet, addr)
	assert.NoError(err)

	maybe, err := decode(str)
	assert.NoError(err)
	assert.Equal(addr, maybe)

}

var allTestAddresses = []string{
	"t00",
	"t01",
	"t010",
	"t0150",
	"t0499",
	"t01024",
	"t01729",
	"t0999999",
	"t15ihq5ibzwki2b4ep2f46avlkrqzhpqgtga7pdrq",
	"t12fiakbhe2gwd5cnmrenekasyn6v5tnaxaqizq6a",
	"t1wbxhu3ypkuo6eyp6hjx6davuelxaxrvwb2kuwva",
	"t1xtwapqc6nh4si2hcwpr3656iotzmlwumogqbuaa",
	"t1xcbgdhkgkwht3hrrnui3jdopeejsoatkzmoltqy",
	"t17uoq6tp427uzv7fztkbsnn64iwotfrristwpryy",
	"t24vg6ut43yw2h2jqydgbg2xq7x6f4kub3bg6as6i",
	"t25nml2cfbljvn4goqtclhifepvfnicv6g7mfmmvq",
	"t2nuqrg7vuysaue2pistjjnt3fadsdzvyuatqtfei",
	"t24dd4ox4c2vpf5vk5wkadgyyn6qtuvgcpxxon64a",
	"t2gfvuyh7v2sx3patm5k23wdzmhyhtmqctasbr23y",
	"t3vvmn62lofvhjd2ugzca6sof2j2ubwok6cj4xxbfzz4yuxfkgobpihhd2thlanmsh3w2ptld2gqkn2jvlss4a",
	"t3wmuu6crofhqmm3v4enos73okk2l366ck6yc4owxwbdtkmpk42ohkqxfitcpa57pjdcftql4tojda2poeruwa",
	"t3s2q2hzhkpiknjgmf4zq3ejab2rh62qbndueslmsdzervrhapxr7dftie4kpnpdiv2n6tvkr743ndhrsw6d3a",
	"t3q22fijmmlckhl56rn5nkyamkph3mcfu5ed6dheq53c244hfmnq2i7efdma3cj5voxenwiummf2ajlsbxc65a",
	"t3u5zgwa4ael3vuocgc5mfgygo4yuqocrntuuhcklf4xzg5tcaqwbyfabxetwtj4tsam3pbhnwghyhijr5mixa",
}

func TestVectorsIDAddress(t *testing.T) {
	testCases := []struct {
		input    uint64
		expected string
	}{
		{uint64(0), "t00"},
		{uint64(1), "t01"},
		{uint64(10), "t010"},
		{uint64(150), "t0150"},
		{uint64(499), "t0499"},
		{uint64(1024), "t01024"},
		{uint64(1729), "t01729"},
		{uint64(999999), "t0999999"},
		{math.MaxUint64, fmt.Sprintf("t0%s", strconv.FormatUint(math.MaxUint64, 10))},
	}

	for _, tc := range testCases {
		tc := tc
		t.Run(fmt.Sprintf("testing actorID address: %s", tc.expected), func(t *testing.T) {
			assert := assert.New(t)

			// Round trip encoding and decoding from string
			addr, err := NewIDAddress(tc.input)
			assert.NoError(err)
			assert.Equal(tc.expected, addr.String())

			maybeAddr, err := NewFromString(tc.expected)
			assert.NoError(err)
			assert.Equal(ID, maybeAddr.Protocol())
			id, _, err := varint.FromUvarint(maybeAddr.Payload())
			assert.NoError(err)
			assert.Equal(tc.input, id)

			// Round trip to and from bytes
			maybeAddrBytes, err := NewFromBytes(maybeAddr.Bytes())
			assert.NoError(err)
			assert.Equal(maybeAddr, maybeAddrBytes)

			// Round trip encoding and decoding json
			b, err := addr.MarshalJSON()
			assert.NoError(err)

			var newAddr Address
			err = newAddr.UnmarshalJSON(b)
			assert.NoError(err)
			assert.Equal(addr, newAddr)
		})
	}

}

func TestSecp256k1Address(t *testing.T) {
	assert := assert.New(t)

	sk, err := crypto.GenerateKey()
	assert.NoError(err)

	addr, err := NewSecp256k1Address(crypto.PublicKey(sk))
	assert.NoError(err)
	assert.Equal(SECP256K1, addr.Protocol())

	str, err := encode(Mainnet, addr)
	assert.NoError(err)

	maybe, err := decode(str)
	assert.NoError(err)
	assert.Equal(addr, maybe)

}

func TestVectorSecp256k1Address(t *testing.T) {
	testCases := []struct {
		input    []byte
		expected string
	}{
		{[]byte{4, 148, 2, 250, 195, 126, 100, 50, 164, 22, 163, 160, 202, 84,
			38, 181, 24, 90, 179, 178, 79, 97, 52, 239, 162, 92, 228, 135, 200,
			45, 46, 78, 19, 191, 69, 37, 17, 224, 210, 36, 84, 33, 248, 97, 59,
			193, 13, 114, 250, 33, 102, 102, 169, 108, 59, 193, 57, 32, 211,
			255, 35, 63, 208, 188, 5},
			"t15ihq5ibzwki2b4ep2f46avlkrqzhpqgtga7pdrq"},

		{[]byte{4, 118, 135, 185, 16, 55, 155, 242, 140, 190, 58, 234, 103, 75,
			18, 0, 12, 107, 125, 186, 70, 255, 192, 95, 108, 148, 254, 42, 34,
			187, 204, 38, 2, 255, 127, 92, 118, 242, 28, 165, 93, 54, 149, 145,
			82, 176, 225, 232, 135, 145, 124, 57, 53, 118, 238, 240, 147, 246,
			30, 189, 58, 208, 111, 127, 218},
			"t12fiakbhe2gwd5cnmrenekasyn6v5tnaxaqizq6a"},
		{[]byte{4, 222, 253, 208, 16, 1, 239, 184, 110, 1, 222, 213, 206, 52,
			248, 71, 167, 58, 20, 129, 158, 230, 65, 188, 182, 11, 185, 41, 147,
			89, 111, 5, 220, 45, 96, 95, 41, 133, 248, 209, 37, 129, 45, 172,
			65, 99, 163, 150, 52, 155, 35, 193, 28, 194, 255, 53, 157, 229, 75,
			226, 135, 234, 98, 49, 155},
			"t1wbxhu3ypkuo6eyp6hjx6davuelxaxrvwb2kuwva"},
		{[]byte{4, 3, 237, 18, 200, 20, 182, 177, 13, 46, 224, 157, 149, 180,
			104, 141, 178, 209, 128, 208, 169, 163, 122, 107, 106, 125, 182, 61,
			41, 129, 30, 233, 115, 4, 121, 216, 239, 145, 57, 233, 18, 73, 202,
			189, 57, 50, 145, 207, 229, 210, 119, 186, 118, 222, 69, 227, 224,
			133, 163, 118, 129, 191, 54, 69, 210},
			"t1xtwapqc6nh4si2hcwpr3656iotzmlwumogqbuaa"},
		{[]byte{4, 247, 150, 129, 154, 142, 39, 22, 49, 175, 124, 24, 151, 151,
			181, 69, 214, 2, 37, 147, 97, 71, 230, 1, 14, 101, 98, 179, 206, 158,
			254, 139, 16, 20, 65, 97, 169, 30, 208, 180, 236, 137, 8, 0, 37, 63,
			166, 252, 32, 172, 144, 251, 241, 251, 242, 113, 48, 164, 236, 195,
			228, 3, 183, 5, 118},
			"t1xcbgdhkgkwht3hrrnui3jdopeejsoatkzmoltqy"},
		{[]byte{4, 66, 131, 43, 248, 124, 206, 158, 163, 69, 185, 3, 80, 222,
			125, 52, 149, 133, 156, 164, 73, 5, 156, 94, 136, 221, 231, 66, 133,
			223, 251, 158, 192, 30, 186, 188, 95, 200, 98, 104, 207, 234, 235,
			167, 174, 5, 191, 184, 214, 142, 183, 90, 82, 104, 120, 44, 248, 111,
			200, 112, 43, 239, 138, 31, 224},
			"t17uoq6tp427uzv7fztkbsnn64iwotfrristwpryy"},
	}

	for _, tc := range testCases {
		tc := tc
		t.Run(fmt.Sprintf("testing secp256k1 address: %s", tc.expected), func(t *testing.T) {
			assert := assert.New(t)

			// Round trip encoding and decoding from string
			addr, err := NewSecp256k1Address(tc.input)
			assert.NoError(err)
			assert.Equal(tc.expected, addr.String())

			maybeAddr, err := NewFromString(tc.expected)
			assert.NoError(err)
			assert.Equal(SECP256K1, maybeAddr.Protocol())
			assert.Equal(addressHash(tc.input), maybeAddr.Payload())

			// Round trip to and from bytes
			maybeAddrBytes, err := NewFromBytes(maybeAddr.Bytes())
			assert.NoError(err)
			assert.Equal(maybeAddr, maybeAddrBytes)

			// Round trip encoding and decoding json
			b, err := addr.MarshalJSON()
			assert.NoError(err)

			var newAddr Address
			err = newAddr.UnmarshalJSON(b)
			assert.NoError(err)
			assert.Equal(addr, newAddr)
		})
	}
}

func TestRandomActorAddress(t *testing.T) {
	assert := assert.New(t)

	actorMsg := make([]byte, 20)
	rand.Read(actorMsg)

	addr, err := NewActorAddress(actorMsg)
	assert.NoError(err)
	assert.Equal(Actor, addr.Protocol())

	str, err := encode(Mainnet, addr)
	assert.NoError(err)

	maybe, err := decode(str)
	assert.NoError(err)
	assert.Equal(addr, maybe)

}

func TestVectorActorAddress(t *testing.T) {
	testCases := []struct {
		input    []byte
		expected string
	}{
		{[]byte{118, 18, 129, 144, 205, 240, 104, 209, 65, 128, 68, 172, 192,
			62, 11, 103, 129, 151, 13, 96},
			"t24vg6ut43yw2h2jqydgbg2xq7x6f4kub3bg6as6i"},
		{[]byte{44, 175, 184, 226, 224, 107, 186, 152, 234, 101, 124, 92, 245,
			244, 32, 35, 170, 35, 232, 142},
			"t25nml2cfbljvn4goqtclhifepvfnicv6g7mfmmvq"},
		{[]byte{2, 44, 158, 14, 162, 157, 143, 64, 197, 106, 190, 195, 92, 141,
			88, 125, 160, 166, 76, 24},
			"t2nuqrg7vuysaue2pistjjnt3fadsdzvyuatqtfei"},
		{[]byte{223, 236, 3, 14, 32, 79, 15, 89, 216, 15, 29, 94, 233, 29, 253,
			6, 109, 127, 99, 189},
			"t24dd4ox4c2vpf5vk5wkadgyyn6qtuvgcpxxon64a"},
		{[]byte{61, 58, 137, 232, 221, 171, 84, 120, 50, 113, 108, 109, 70, 140,
			53, 96, 201, 244, 127, 216},
			"t2gfvuyh7v2sx3patm5k23wdzmhyhtmqctasbr23y"},
	}

	for _, tc := range testCases {
		tc := tc
		t.Run(fmt.Sprintf("testing Actor address: %s", tc.expected), func(t *testing.T) {
			assert := assert.New(t)

			// Round trip encoding and decoding from string
			addr, err := NewActorAddress(tc.input)
			assert.NoError(err)
			assert.Equal(tc.expected, addr.String())

			maybeAddr, err := NewFromString(tc.expected)
			assert.NoError(err)
			assert.Equal(Actor, maybeAddr.Protocol())
			assert.Equal(addressHash(tc.input), maybeAddr.Payload())

			// Round trip to and from bytes
			maybeAddrBytes, err := NewFromBytes(maybeAddr.Bytes())
			assert.NoError(err)
			assert.Equal(maybeAddr, maybeAddrBytes)

			// Round trip encoding and decoding json
			b, err := addr.MarshalJSON()
			assert.NoError(err)

			var newAddr Address
			err = newAddr.UnmarshalJSON(b)
			assert.NoError(err)
			assert.Equal(addr, newAddr)
		})
	}
}

func TestRandomBLSAddress(t *testing.T) {
	assert := assert.New(t)

	pk := ffi.PrivateKeyPublicKey(ffi.PrivateKeyGenerate())

	addr, err := NewBLSAddress(pk[:])
	assert.NoError(err)
	assert.Equal(BLS, addr.Protocol())

	str, err := encode(Mainnet, addr)
	assert.NoError(err)

	maybe, err := decode(str)
	assert.NoError(err)
	assert.Equal(addr, maybe)

}

func TestVectorBLSAddress(t *testing.T) {
	testCases := []struct {
		input    []byte
		expected string
	}{
		{[]byte{173, 88, 223, 105, 110, 45, 78, 145, 234, 134, 200, 129, 233, 56,
			186, 78, 168, 27, 57, 94, 18, 121, 123, 132, 185, 207, 49, 75, 149, 70,
			112, 94, 131, 156, 122, 153, 214, 6, 178, 71, 221, 180, 249, 172, 122,
			52, 20, 221},
			"t3vvmn62lofvhjd2ugzca6sof2j2ubwok6cj4xxbfzz4yuxfkgobpihhd2thlanmsh3w2ptld2gqkn2jvlss4a"},
		{[]byte{179, 41, 79, 10, 46, 41, 224, 198, 110, 188, 35, 93, 47, 237,
			202, 86, 151, 191, 120, 74, 246, 5, 199, 90, 246, 8, 230, 166, 61, 92,
			211, 142, 168, 92, 168, 152, 158, 14, 253, 233, 24, 139, 56, 47,
			147, 114, 70, 13},
			"t3wmuu6crofhqmm3v4enos73okk2l366ck6yc4owxwbdtkmpk42ohkqxfitcpa57pjdcftql4tojda2poeruwa"},
		{[]byte{150, 161, 163, 228, 234, 122, 20, 212, 153, 133, 230, 97, 178,
			36, 1, 212, 79, 237, 64, 45, 29, 9, 37, 178, 67, 201, 35, 88, 156,
			15, 188, 126, 50, 205, 4, 226, 158, 215, 141, 21, 211, 125, 58, 170,
			63, 230, 218, 51},
			"t3s2q2hzhkpiknjgmf4zq3ejab2rh62qbndueslmsdzervrhapxr7dftie4kpnpdiv2n6tvkr743ndhrsw6d3a"},
		{[]byte{134, 180, 84, 37, 140, 88, 148, 117, 247, 209, 111, 90, 172, 1,
			138, 121, 246, 193, 22, 157, 32, 252, 51, 146, 29, 216, 181, 206, 28,
			172, 108, 52, 143, 144, 163, 96, 54, 36, 246, 174, 185, 27, 100, 81,
			140, 46, 128, 149},
			"t3q22fijmmlckhl56rn5nkyamkph3mcfu5ed6dheq53c244hfmnq2i7efdma3cj5voxenwiummf2ajlsbxc65a"},
		{[]byte{167, 114, 107, 3, 128, 34, 247, 90, 56, 70, 23, 88, 83, 96, 206,
			230, 41, 7, 10, 45, 157, 40, 113, 41, 101, 229, 242, 110, 204, 64,
			133, 131, 130, 128, 55, 36, 237, 52, 242, 114, 3, 54, 240, 157, 182,
			49, 240, 116},
			"t3u5zgwa4ael3vuocgc5mfgygo4yuqocrntuuhcklf4xzg5tcaqwbyfabxetwtj4tsam3pbhnwghyhijr5mixa"},
	}

	for _, tc := range testCases {
		tc := tc
		t.Run(fmt.Sprintf("testing bls address: %s", tc.expected), func(t *testing.T) {
			assert := assert.New(t)

			// Round trip encoding and decoding from string
			addr, err := NewBLSAddress(tc.input)
			assert.NoError(err)
			assert.Equal(tc.expected, addr.String())

			maybeAddr, err := NewFromString(tc.expected)
			assert.NoError(err)
			assert.Equal(BLS, maybeAddr.Protocol())
			assert.Equal(tc.input, maybeAddr.Payload())

			// Round trip to and from bytes
			maybeAddrBytes, err := NewFromBytes(maybeAddr.Bytes())
			assert.NoError(err)
			assert.Equal(maybeAddr, maybeAddrBytes)

			// Round trip encoding and decoding json
			b, err := addr.MarshalJSON()
			assert.NoError(err)

			var newAddr Address
			err = newAddr.UnmarshalJSON(b)
			assert.NoError(err)
			assert.Equal(addr, newAddr)
		})
	}
}

func TestInvalidStringAddresses(t *testing.T) {
	testCases := []struct {
		input    string
		expetErr error
	}{
		{"Q2gfvuyh7v2sx3patm5k23wdzmhyhtmqctasbr23y", ErrUnknownNetwork},
		{"t4gfvuyh7v2sx3patm5k23wdzmhyhtmqctasbr23y", ErrUnknownProtocol},
		{"t2gfvuyh7v2sx3patm5k23wdzmhyhtmqctasbr24y", ErrInvalidChecksum},
		{"t0banananananannnnnnnnn", ErrInvalidLength},
		{"t0banananananannnnnnnn", ErrInvalidPayload},
		{"t2gfvuyh7v2sx3patm1k23wdzmhyhtmqctasbr24y", base32.CorruptInputError(16)}, // '1' is not in base32 alphabet
		{"t2gfvuyh7v2sx3paTm1k23wdzmhyhtmqctasbr24y", base32.CorruptInputError(14)}, // 'T' is not in base32 alphabet
		{"t2", ErrInvalidLength},
	}

	for _, tc := range testCases {
		tc := tc
		t.Run(fmt.Sprintf("testing string address: %s", tc.expetErr), func(t *testing.T) {
			assert := assert.New(t)

			_, err := NewFromString(tc.input)
			assert.Equal(tc.expetErr, err)
		})
	}

}

func TestInvalidByteAddresses(t *testing.T) {
	testCases := []struct {
		input    []byte
		expetErr error
	}{
		// Unknown Protocol
		{[]byte{4, 4, 4}, ErrUnknownProtocol},

		// ID protocol
		{[]byte{0}, ErrInvalidLength},

		// SECP256K1 Protocol
		{append([]byte{1}, make([]byte, PayloadHashLength-1)...), ErrInvalidPayload},
		{append([]byte{1}, make([]byte, PayloadHashLength+1)...), ErrInvalidPayload},
		// Actor Protocol
		{append([]byte{2}, make([]byte, PayloadHashLength-1)...), ErrInvalidPayload},
		{append([]byte{2}, make([]byte, PayloadHashLength+1)...), ErrInvalidPayload},

		// BLS Protocol
		{append([]byte{3}, make([]byte, ffi.PublicKeyBytes-1)...), ErrInvalidPayload},
		{append([]byte{3}, make([]byte, ffi.PrivateKeyBytes+1)...), ErrInvalidPayload},
	}

	for _, tc := range testCases {
		tc := tc
		t.Run(fmt.Sprintf("testing byte address: %s", tc.expetErr), func(t *testing.T) {
			assert := assert.New(t)

			_, err := NewFromBytes(tc.input)
			assert.Equal(tc.expetErr, err)
		})
	}

}

func TestChecksum(t *testing.T) {
	assert := assert.New(t)

	data := []byte("helloworld")
	bata := []byte("kittinmittins")

	cksm := Checksum(data)
	assert.Len(cksm, ChecksumHashLength)

	assert.True(ValidateChecksum(data, cksm))
	assert.False(ValidateChecksum(bata, cksm))

}

func TestAddressFormat(t *testing.T) {
	assert := assert.New(t)
	require := require.New(t)

	a, err := NewActorAddress([]byte("hello"))
	require.NoError(err)

	assert.Equal("t2wvjry4bx6bwj6kkhcmvgu5zafqyi5cjzbtet3va", a.String())
	assert.Equal("02B5531C7037F06C9F2947132A6A77202C308E8939", fmt.Sprintf("%X", a))
	assert.Equal("[2 - b5531c7037f06c9f2947132a6a77202c308e8939]", fmt.Sprintf("%v", a))

	assert.Equal("", fmt.Sprintf("%X", Undef))
	assert.Equal(UndefAddressString, Undef.String())
	assert.Equal(UndefAddressString, fmt.Sprintf("%v", Undef))
}

func TestCborMarshal(t *testing.T) {
	for _, a := range allTestAddresses {
		addr, err := NewFromString(a)
		if err != nil {
			t.Fatal(err)
		}

		buf := new(bytes.Buffer)
		if err := addr.MarshalCBOR(buf); err != nil {
			t.Fatal(err)
		}

		/*
			// Note: this is commented out because we're currently serializing addresses as cbor "text strings", not "byte strings".
			// This is to get around the restriction that refmt only allows string keys in maps.
			// if you change it to serialize to byte strings and uncomment this, the tests pass fine
			oldbytes, err := cbor.DumpObject(addr)
			if err != nil {
				t.Fatal(err)
			}

			if !bytes.Equal(oldbytes, buf.Bytes()) {
				t.Fatalf("serialization doesnt match old serialization: %s", a)
			}
		*/

		var out Address
		if err := out.UnmarshalCBOR(buf); err != nil {
			t.Fatal(err)
		}

		if out != addr {
			t.Fatalf("failed to round trip %s", a)
		}
	}
}

func BenchmarkCborMarshal(b *testing.B) {
	addr, err := NewFromString("t15ihq5ibzwki2b4ep2f46avlkrqzhpqgtga7pdrq")
	if err != nil {
		b.Fatal(err)
	}

	b.ReportAllocs()
	b.ResetTimer()

	buf := new(bytes.Buffer)
	for i := 0; i < b.N; i++ {
		buf.Reset()
		if err := addr.MarshalCBOR(buf); err != nil {
			b.Fatal(err)
		}
	}
}

func BenchmarkCborUnmarshal(b *testing.B) {
	addr, err := NewFromString("t15ihq5ibzwki2b4ep2f46avlkrqzhpqgtga7pdrq")
	if err != nil {
		b.Fatal(err)
	}

	buf := new(bytes.Buffer)
	if err := addr.MarshalCBOR(buf); err != nil {
		b.Fatal(err)
	}

	b.ReportAllocs()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		var a Address
		if err := a.UnmarshalCBOR(bytes.NewReader(buf.Bytes())); err != nil {
			b.Fatal(err)
		}
	}
}

func TestIDEdgeCase(t *testing.T) {
	a, err := NewFromBytes([]byte{0, 0x80})
	_ = a.String()
	assert.Error(t, err)
}