//go:build unittest

package pivx

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"math/big"
	"os"
	"path/filepath"
	"reflect"
	"testing"

	"github.com/martinboehm/btcutil/chaincfg"
	"github.com/trezor/blockbook/bchain"
	"github.com/trezor/blockbook/bchain/coins/btc"
)

func TestMain(m *testing.M) {
	c := m.Run()
	chaincfg.ResetParams()
	os.Exit(c)
}

func Test_GetAddrDescFromAddress_Mainnet(t *testing.T) {
	type args struct {
		address string
	}
	tests := []struct {
		name    string
		args    args
		want    string
		wantErr bool
	}{
		{
			name:    "P2PKH1",
			args:    args{address: "DRM8TaiY38qcHbgdytp8oETreobBLHtpeE"},
			want:    "76a914dda91c0396050d660f9c0e38f78064486bbfcb2c88ac",
			wantErr: false,
		},
	}
	parser := NewPivXParser(GetChainParams("main"), &btc.Configuration{})

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := parser.GetAddrDescFromAddress(tt.args.address)
			if (err != nil) != tt.wantErr {
				t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			h := hex.EncodeToString(got)
			if !reflect.DeepEqual(h, tt.want) {
				t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want)
			}
		})
	}
}

func Test_GetAddressesFromAddrDesc(t *testing.T) {
	type args struct {
		script string
	}
	tests := []struct {
		name    string
		args    args
		want    []string
		want2   bool
		wantErr bool
	}{
		{
			name:    "P2PKH1",
			args:    args{script: "76a914dda91c0396050d660f9c0e38f78064486bbfcb2c88ac"},
			want:    []string{"DRM8TaiY38qcHbgdytp8oETreobBLHtpeE"},
			want2:   true,
			wantErr: false,
		},
		{
			name:    "pubkey",
			args:    args{script: "210251c5555ff3c684aebfca92f5329e2f660da54856299da067060a1bcf5e8fae73ac"},
			want:    []string{"DKL3QzCbJqrHpRKAHvEqsomsDhkQPvVzZg"},
			want2:   false,
			wantErr: false,
		},
		{
			name:    "zerocoinspend",
			args:    args{script: "[encoded data, 40568 characters]"},
			want:    []string{"Zerocoin Spend"},
			want2:   false,
			wantErr: false,
		},
		{
			name:    "zerocoinmint",
			args:    args{script: "c10281004c81c92ce491443c7c4fb4794c83c459353002cec18382098392ecd43d62519f9db08151e0176c864794723c22851580de24f24183dbb44a7cc91ceb1a34524680c42a10b0f1a1f846f740d1bf16940636b1bd7cdfe28834d8b4b312a13442f01ed4b9ed20aae6ed0a4c39aa9697ab6eb4aea90013aee86cc10862490390ba53b7d100"},
			want:    []string{"Zerocoin Mint"},
			want2:   false,
			wantErr: false,
		},
	}

	parser := NewPivXParser(GetChainParams("main"), &btc.Configuration{})

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			b, _ := hex.DecodeString(tt.args.script)
			got, got2, err := parser.GetAddressesFromAddrDesc(b)
			if (err != nil) != tt.wantErr {
				t.Errorf("GetAddressesFromAddrDesc() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("GetAddressesFromAddrDesc() = %v, want %v", got, tt.want)
			}
			if !reflect.DeepEqual(got2, tt.want2) {
				t.Errorf("GetAddressesFromAddrDesc() = %v, want %v", got2, tt.want2)
			}
		})
	}
}

var (
	// regular transaction
	testTx1       bchain.Tx
	testTxPacked1 = "0a2052b116d26f7c8b633c284f8998a431e106d837c0c5888f9ea5273d36c4556bec12f501010000000188557c816acd0a61579b701278c7dde85ea25d57877f9dbc65d3b2df2feacc42320000006b483045022100f5d0e98d064d5256852e420a4a3779527fb182c5edbfecf6143fc70eeba8eeef02202f0b2445185fbf846cca07c56c317733a9a4e46f960615f541da7aa27c33cfa201210251c5555ff3c684aebfca92f5329e2f660da54856299da067060a1bcf5e8fae73ffffffff03000000000000000000f06832fa0100000023210251c5555ff3c684aebfca92f5329e2f660da54856299da067060a1bcf5e8fae73aca038370e000000001976a914b4aa56c103b398f875bb8d15c3bb4136aa62725f88ac000000001883a8aacd0520002880ea303299010a00122042ccea2fdfb2d365bc9d7f87575da25ee8ddc77812709b57610acd6a817c55881832226b483045022100f5d0e98d064d5256852e420a4a3779527fb182c5edbfecf6143fc70eeba8eeef02202f0b2445185fbf846cca07c56c317733a9a4e46f960615f541da7aa27c33cfa201210251c5555ff3c684aebfca92f5329e2f660da54856299da067060a1bcf5e8fae7328ffffffff0f3a0210003a520a0501fa3268f010011a23210251c5555ff3c684aebfca92f5329e2f660da54856299da067060a1bcf5e8fae73ac2222444b4c33517a43624a71724870524b4148764571736f6d7344686b515076567a5a673a470a040e3738a010021a1976a914b4aa56c103b398f875bb8d15c3bb4136aa62725f88ac2222444d634e45393855667571454b32674746664b4234597057415771627748524154484000"

	// transaction with OP_ZEROCOINMINT
	testTx2       bchain.Tx
	testTxPacked2 = "0a20599d5d797a4575eb25e1c291c0e7630bd6fdc0e6ec5fa9b14147f929a4e41bf212ae020100000001b56a0fe242a8de7dcbb58ae1009e44e7f2ec25a65eeb8b815cf53393309741ca0100000049483045022100cc208a59341dca98207ec8a4a42c014d435192694a77c69d40e51467800c0a0802205ac1782d4ecefa260b33340d92c2ab2396b43c1073a67b4180aa8ef2aede8af801ffffffff0200e876481700000087c10281004c816f5ce1eeda911203319a256e8560c8dbfd47b569ff32c27559bda78854e63e49718ce43036e5120dce357b5630afd745d399f91e675a921adbb45224a6661656217fcfe32396fb25609b724646759116326964f2f1f7ddb7c340dc24be2b75a0a9dc05ca2fdf805c03c7a04d972456beb82a51de73d8842b39a553919dfa5d8e003e98dab7210000001976a914dda91c0396050d660f9c0e38f78064486bbfcb2c88ac00000000189dab96cf05200028b8dc3432770a001220ca4197309333f55c818beb5ea625ecf2e7449e00e18ab5cb7ddea842e20f6ab518012249483045022100cc208a59341dca98207ec8a4a42c014d435192694a77c69d40e51467800c0a0802205ac1782d4ecefa260b33340d92c2ab2396b43c1073a67b4180aa8ef2aede8af80128ffffffff0f3a93010a05174876e80010001a8701c10281004c816f5ce1eeda911203319a256e8560c8dbfd47b569ff32c27559bda78854e63e49718ce43036e5120dce357b5630afd745d399f91e675a921adbb45224a6661656217fcfe32396fb25609b724646759116326964f2f1f7ddb7c340dc24be2b75a0a9dc05ca2fdf805c03c7a04d972456beb82a51de73d8842b39a553919dfa5d8e003a480a0521b7da983e10011a1976a914dda91c0396050d660f9c0e38f78064486bbfcb2c88ac222244524d38546169593338716348626764797470386f455472656f62424c48747065454000"

	// transaction with OP_ZEROCOINSPEND
	testTx3       bchain.Tx
	testTxPacked3 = "[encoded data, 88124 characters]"
)

func init() {
	testTx1 = bchain.Tx{
		Hex:      "010000000188557c816acd0a61579b701278c7dde85ea25d57877f9dbc65d3b2df2feacc42320000006b483045022100f5d0e98d064d5256852e420a4a3779527fb182c5edbfecf6143fc70eeba8eeef02202f0b2445185fbf846cca07c56c317733a9a4e46f960615f541da7aa27c33cfa201210251c5555ff3c684aebfca92f5329e2f660da54856299da067060a1bcf5e8fae73ffffffff03000000000000000000f06832fa0100000023210251c5555ff3c684aebfca92f5329e2f660da54856299da067060a1bcf5e8fae73aca038370e000000001976a914b4aa56c103b398f875bb8d15c3bb4136aa62725f88ac00000000",
		Txid:     "52b116d26f7c8b633c284f8998a431e106d837c0c5888f9ea5273d36c4556bec",
		LockTime: 0,
		Vin: []bchain.Vin{
			{
				ScriptSig: bchain.ScriptSig{
					Hex: "483045022100f5d0e98d064d5256852e420a4a3779527fb182c5edbfecf6143fc70eeba8eeef02202f0b2445185fbf846cca07c56c317733a9a4e46f960615f541da7aa27c33cfa201210251c5555ff3c684aebfca92f5329e2f660da54856299da067060a1bcf5e8fae73",
				},
				Txid:     "42ccea2fdfb2d365bc9d7f87575da25ee8ddc77812709b57610acd6a817c5588",
				Vout:     50,
				Sequence: 4294967295,
			},
		},
		Vout: []bchain.Vout{
			{
				ValueSat: *big.NewInt(0),
				N:        0,
				ScriptPubKey: bchain.ScriptPubKey{
					Hex: "",
				},
			},
			{
				ValueSat: *big.NewInt(8492574960),
				N:        1,
				ScriptPubKey: bchain.ScriptPubKey{
					Hex: "210251c5555ff3c684aebfca92f5329e2f660da54856299da067060a1bcf5e8fae73ac",
					Addresses: []string{
						"DKL3QzCbJqrHpRKAHvEqsomsDhkQPvVzZg",
					},
				},
			},
			{
				ValueSat: *big.NewInt(238500000),
				N:        2,
				ScriptPubKey: bchain.ScriptPubKey{
					Hex: "76a914b4aa56c103b398f875bb8d15c3bb4136aa62725f88ac",
					Addresses: []string{
						"DMcNE98UfuqEK2gGFfKB4YpWAWqbwHRATH",
					},
				},
			},
		},
		Blocktime: 1504351235,
		Time:      1504351235,
	}

	testTx2 = bchain.Tx{
		Hex:      "0100000001b56a0fe242a8de7dcbb58ae1009e44e7f2ec25a65eeb8b815cf53393309741ca0100000049483045022100cc208a59341dca98207ec8a4a42c014d435192694a77c69d40e51467800c0a0802205ac1782d4ecefa260b33340d92c2ab2396b43c1073a67b4180aa8ef2aede8af801ffffffff0200e876481700000087c10281004c816f5ce1eeda911203319a256e8560c8dbfd47b569ff32c27559bda78854e63e49718ce43036e5120dce357b5630afd745d399f91e675a921adbb45224a6661656217fcfe32396fb25609b724646759116326964f2f1f7ddb7c340dc24be2b75a0a9dc05ca2fdf805c03c7a04d972456beb82a51de73d8842b39a553919dfa5d8e003e98dab7210000001976a914dda91c0396050d660f9c0e38f78064486bbfcb2c88ac00000000",
		Txid:     "599d5d797a4575eb25e1c291c0e7630bd6fdc0e6ec5fa9b14147f929a4e41bf2",
		LockTime: 0,
		Vin: []bchain.Vin{
			{
				ScriptSig: bchain.ScriptSig{
					Hex: "483045022100cc208a59341dca98207ec8a4a42c014d435192694a77c69d40e51467800c0a0802205ac1782d4ecefa260b33340d92c2ab2396b43c1073a67b4180aa8ef2aede8af801",
				},
				Txid:     "ca4197309333f55c818beb5ea625ecf2e7449e00e18ab5cb7ddea842e20f6ab5",
				Vout:     1,
				Sequence: 4294967295,
			},
		},
		Vout: []bchain.Vout{
			{
				ValueSat: *big.NewInt(100000000000),
				N:        0,
				ScriptPubKey: bchain.ScriptPubKey{
					Hex: "c10281004c816f5ce1eeda911203319a256e8560c8dbfd47b569ff32c27559bda78854e63e49718ce43036e5120dce357b5630afd745d399f91e675a921adbb45224a6661656217fcfe32396fb25609b724646759116326964f2f1f7ddb7c340dc24be2b75a0a9dc05ca2fdf805c03c7a04d972456beb82a51de73d8842b39a553919dfa5d8e00",
				},
			},
			{
				ValueSat: *big.NewInt(144818477118),
				N:        1,
				ScriptPubKey: bchain.ScriptPubKey{
					Hex: "76a914dda91c0396050d660f9c0e38f78064486bbfcb2c88ac",
					Addresses: []string{
						"DRM8TaiY38qcHbgdytp8oETreobBLHtpeE",
					},
				},
			},
		},
		Blocktime: 1508218269,
		Time:      1508218269,
	}

	testTx3 = bchain.Tx{
		Hex:      "[encoded data, 43970 characters]",
		Txid:     "b65181decb00e684fef238776a0a129db4e1ffdfc454f6ef323e5f7a8deae6a8",
		LockTime: 0,
		Vin: []bchain.Vin{
			{
				ScriptSig: bchain.ScriptSig{
					Hex: "[encoded data, 43796 characters]",
				},
				Txid:     "0000000000000000000000000000000000000000000000000000000000000000",
				Vout:     4294967295,
				Sequence: 1000,
			},
		},
		Vout: []bchain.Vout{
			{
				ValueSat: *big.NewInt(100000000000),
				N:        0,
				ScriptPubKey: bchain.ScriptPubKey{
					Hex: "76a914dbe6d470fa9fe4d037043533eff4f80aeef0c8d288ac",
					Addresses: []string{
						"DRBq3cEq23QYUAmHsocC6fDRvJE7Sr5HEZ",
					},
				},
			},
		},
		Blocktime: 1534045750,
		Time:      1534045750,
	}
}

func Test_PackTx(t *testing.T) {
	type args struct {
		tx        bchain.Tx
		height    uint32
		blockTime int64
		parser    *PivXParser
	}
	tests := []struct {
		name    string
		args    args
		want    string
		wantErr bool
	}{
		{
			name: "pivx-1",
			args: args{
				tx:        testTx1,
				height:    800000,
				blockTime: 1504351235,
				parser:    NewPivXParser(GetChainParams("main"), &btc.Configuration{}),
			},
			want:    testTxPacked1,
			wantErr: false,
		},
		{
			name: "pivx-2",
			args: args{
				tx:        testTx2,
				height:    863800,
				blockTime: 1508218269,
				parser:    NewPivXParser(GetChainParams("main"), &btc.Configuration{}),
			},
			want:    testTxPacked2,
			wantErr: false,
		},
		{
			name: "pivx-3",
			args: args{
				tx:        testTx3,
				height:    1300002,
				blockTime: 1534045750,
				parser:    NewPivXParser(GetChainParams("main"), &btc.Configuration{}),
			},
			want:    testTxPacked3,
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := tt.args.parser.PackTx(&tt.args.tx, tt.args.height, tt.args.blockTime)
			if (err != nil) != tt.wantErr {
				t.Errorf("packTx() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			h := hex.EncodeToString(got)
			if !reflect.DeepEqual(h, tt.want) {
				t.Errorf("packTx() = %v, want %v", h, tt.want)
			}
		})
	}
}

func Test_UnpackTx(t *testing.T) {
	type args struct {
		packedTx string
		parser   *PivXParser
	}
	tests := []struct {
		name    string
		args    args
		want    *bchain.Tx
		want1   uint32
		wantErr bool
	}{
		{
			name: "pivx-1",
			args: args{
				packedTx: testTxPacked1,
				parser:   NewPivXParser(GetChainParams("main"), &btc.Configuration{}),
			},
			want:    &testTx1,
			want1:   800000,
			wantErr: false,
		},
		{
			name: "pivx-2",
			args: args{
				packedTx: testTxPacked2,
				parser:   NewPivXParser(GetChainParams("main"), &btc.Configuration{}),
			},
			want:    &testTx2,
			want1:   863800,
			wantErr: false,
		},
		{
			name: "pivx-3",
			args: args{
				packedTx: testTxPacked3,
				parser:   NewPivXParser(GetChainParams("main"), &btc.Configuration{}),
			},
			want:    &testTx3,
			want1:   1300002,
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			b, _ := hex.DecodeString(tt.args.packedTx)
			got, got1, err := tt.args.parser.UnpackTx(b)
			if (err != nil) != tt.wantErr {
				t.Errorf("unpackTx() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("unpackTx() got = %v, want %v", got, tt.want)
			}
			if got1 != tt.want1 {
				t.Errorf("unpackTx() got1 = %v, want %v", got1, tt.want1)
			}
		})
	}
}

type testBlock struct {
	size int
	time int64
	txs  []string
}

var testParseBlockTxs = map[int]testBlock{
	800000: {
		size: 463,
		time: 1504351235,
		txs: []string{
			"556569e1bd20ae007853d839fda5cbefed4883ac53e6327a0a8a30180d242e24",
			"52b116d26f7c8b633c284f8998a431e106d837c0c5888f9ea5273d36c4556bec",
		},
	},
	864611: {
		size: 460,
		time: 1508265763,
		txs: []string{
			"3e68db3117021178bfd56eceed2fcfcc8210d1198372412de713f953ce01db5c",
			"f773cd0f042a264c31c8e0b70485e2c85097b66d3d67bac055d620505de29ce2",
		},
	},
	1300002: {
		size: 43673,
		time: 1534045750,
		txs: []string{
			"21cfc7b58e666c393431619393526fd736d8d6bf2d8699e2acbedbf5d3c60b85",
			"afdc7e28ca02ff6a71fc662072bf7590bac6ec3bd93148d0c9f9d1a820c6b1ed",
			"b65181decb00e684fef238776a0a129db4e1ffdfc454f6ef323e5f7a8deae6a8",
			"10bc6050c6eb19dd81bede01e974f46740a94049902ced66f10b6dd3d5d31da5",
		},
	},
	1299975: {
		size: 42801,
		time: 1534044012,
		txs: []string{
			"a99f5fa0b4bf464d57ddd10ae56db27edb054830a2b95b9a23509d217b871a6a",
			"fccaee46b6f01e42c24ee3f7bbfe5a3f2732b2486af0423473446fbc50a1e48c",
			"baab318237b55f2dbb1ede2c65f7da9bf2c51c0e9a63903b3376f559fe999960",
		},
	},
	1384217: {
		size: 430,
		time: 1577435460,
		txs: []string{
			"9c64e40fb27da7c7f826076d0f073968ce36188e827f5a7b2924974369bf9433",
			"0c5b5b1b9c491a5a7ec9caab204e14f72383460b887c5342d296ae0a7451c09e",
		},
	},
}

func helperLoadBlock(t *testing.T, height int) []byte {
	name := fmt.Sprintf("block_dump.%d", height)
	path := filepath.Join("testdata", name)

	d, err := ioutil.ReadFile(path)
	if err != nil {
		t.Fatal(err)
	}

	d = bytes.TrimSpace(d)

	b := make([]byte, hex.DecodedLen(len(d)))
	_, err = hex.Decode(b, d)
	if err != nil {
		t.Fatal(err)
	}

	return b
}

func TestParseBlock(t *testing.T) {
	p := NewPivXParser(GetChainParams("main"), &btc.Configuration{})

	for height, tb := range testParseBlockTxs {
		b := helperLoadBlock(t, height)

		blk, err := p.ParseBlock(b)
		if err != nil {
			t.Fatal(err)
		}

		if blk.Size != tb.size {
			t.Errorf("ParseBlock() block size: got %d, want %d", blk.Size, tb.size)
		}

		if blk.Time != tb.time {
			t.Errorf("ParseBlock() block time: got %d, want %d", blk.Time, tb.time)
		}

		if len(blk.Txs) != len(tb.txs) {
			t.Errorf("ParseBlock() number of transactions: got %d, want %d", len(blk.Txs), len(tb.txs))
		}

		for ti, tx := range tb.txs {
			if blk.Txs[ti].Txid != tx {
				t.Errorf("ParseBlock() transaction %d: got %s, want %s", ti, blk.Txs[ti].Txid, tx)
			}
		}
	}
}
