use Test2::V0;
use Bitcoin::Crypto::Key::ExtPrivate;
use Bitcoin::Crypto::Key::ExtPublic;
use Bitcoin::Crypto::Util qw(to_format);

my @test_data_private = (
	{
		seed => '000102030405060708090a0b0c0d0e0f',
		data => [
			{
				path => "m",
				private =>
					'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi',
				public =>
					'xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8',
			},
			{
				path => "m/0'",
				private =>
					'xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7',
				public =>
					'xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw',
			},
			{
				path => "m/0'/1",
				private =>
					'xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs',
				public =>
					'xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ',
			},
			{
				path => "m/0'/1/2'",
				private =>
					'xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM',
				public =>
					'xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5',
			},
			{
				path => "m/0'/1/2'/2",
				private =>
					'xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334',
				public =>
					'xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV'
			},
			{
				path => "m/0'/1/2'/2/1000000000",
				private =>
					'xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76',
				public =>
					'xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy'
			}
		],
	},

	{
		seed =>
			'fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542',
		data => [
			{
				path => "m",
				private =>
					'xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U',
				public =>
					'xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB',
			},
			{
				path => "m/0",
				private =>
					'xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt',
				public =>
					'xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH',
			},
			{
				path => "m/0/2147483647'",
				private =>
					'xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9',
				public =>
					'xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a',
			},
			{
				path => "m/0/2147483647'/1",
				private =>
					'xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef',
				public =>
					'xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon',
			},
			{
				path => "m/0/2147483647'/1/2147483646'",
				private =>
					'xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc',
				public =>
					'xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL',
			},
			{
				path => "m/0/2147483647'/1/2147483646'/2",
				private =>
					'xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j',
				public =>
					'xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt',
			},
			{
				path => "m/0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20",
				private =>
					'xprvAYbD8i8eLErNc8riuDfzfK2RP4X5LrvzBDymhNadcDW7gbd2JQQZhxYYUCYYpHVA6RXRdeZmWZoW9eYe6Ai6QAaFpAqo8XPw64JrDE78ka1',
				public =>
					'xpub6maZYDfYAcQfpcwC1FD12Sy9w6MZkKeqYSuNVkzFAZ36ZPxAqwipFks2KTDxS8c926h9Nzmj5RCzd5LXU7YCyoqGjdgkLEmzuBWiCwYpJ5m',
			},
			{
				path =>
					"m/0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/133'/134'/135'/136'/137'/138'/139'/140/141'/142/143'/144/145'",
				private =>
					'xprvAyLdMHTCevkkuxcd1oriqGPaDMrpLjY1gUKdj34L9rnRCp7St7a767kXGYFELbKnCrqqRu4Yy331fDQMxXZshXfUctKfquHBh2ydGZnZC4i',
				public =>
					'xpub7CKyknz6VJK48Sh67qPjCQLJmPhJkCFs3hFEXRTwiCKQ5cSbRetMdv517nY55HZsjFWcUZowJAzKNKNEL9PQsYo8QELXFVgvp7azxdao5H9',
			}
		],
	},

	{
		seed =>
			'4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be',
		data => [
			{
				path => "m",
				private =>
					'xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6',
				public =>
					'xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13',
			},
			{
				path => "m/0'",
				private =>
					'xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L',
				public =>
					'xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y',
			}
		],
	},

	{
		seed => '3ddd5602285899a946114506157c7997e5444528f3003f6134712147db19b678',
		data => [
			{
				path => "m",
				private =>
					'xprv9s21ZrQH143K48vGoLGRPxgo2JNkJ3J3fqkirQC2zVdk5Dgd5w14S7fRDyHH4dWNHUgkvsvNDCkvAwcSHNAQwhwgNMgZhLtQC63zxwhQmRv',
				public =>
					'xpub661MyMwAqRbcGczjuMoRm6dXaLDEhW1u34gKenbeYqAix21mdUKJyuyu5F1rzYGVxyL6tmgBUAEPrEz92mBXjByMRiJdba9wpnN37RLLAXa',
			},
			{
				path => "m/0'",
				private =>
					'xprv9vB7xEWwNp9kh1wQRfCCQMnZUEG21LpbR9NPCNN1dwhiZkjjeGRnaALmPXCX7SgjFTiCTT6bXes17boXtjq3xLpcDjzEuGLQBM5ohqkao9G',
				public =>
					'xpub69AUMk3qDBi3uW1sXgjCmVjJ2G6WQoYSnNHyzkmdCHEhSZ4tBok37xfFEqHd2AddP56Tqp4o56AePAgCjYdvpW2PU2jbUPFKsav5ut6Ch1m',
			},
			{
				path => "m/0'/1'",
				private =>
					'xprv9xJocDuwtYCMNAo3Zw76WENQeAS6WGXQ55RCy7tDJ8oALr4FWkuVoHJeHVAcAqiZLE7Je3vZJHxspZdFHfnBEjHqU5hG1Jaj32dVoS6XLT1',
				public =>
					'xpub6BJA1jSqiukeaesWfxe6sNK9CCGaujFFSJLomWHprUL9DePQ4JDkM5d88n49sMGJxrhpjazuXYWdMf17C9T5XnxkopaeS7jGk1GyyVziaMt',
			}
		],
	},
);

my @test_data_public = (
	{
		parent =>
			'xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8',
		path => "M",
		child =>
			'xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8',
	},

	{
		parent =>
			'xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5',
		path => "M/2/1000000000",
		child =>
			'xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy',
	},

	{
		parent =>
			'xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB',
		path => "M/0",
		child =>
			'xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH',
	},

	{
		parent =>
			'xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw',
		path => "M/1",
		child =>
			'xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ',
	},

	{
		parent =>
			'xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV',
		path => "M/1000000000",
		child =>
			'xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy',
	}
);

my @test_data_error = (
	[
		'xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV',
		"m"
	],

	[
		'xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV',
		"m/"
	],

	[
		'xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV',
		"M/"
	],

	['xxyz', "m/1'/1"],

	[
		'xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4',
		"m/1'/1"
	],
);

my $case_num = 0;
for my $tdata (@test_data_private) {
	subtest "testing private data, case $case_num" => sub {
		my $test_vector = $tdata->{data};
		my $base_key = Bitcoin::Crypto::Key::ExtPrivate->from_seed([hex => $tdata->{seed}]);
		for my $tdata (@$test_vector) {
			my $key = $base_key->derive_key($tdata->{path});
			my $pubkey = $key->get_public_key();
			is(to_format [base58 => $key->to_serialized], $tdata->{private}, 'private key ok');
			is(to_format [base58 => $pubkey->to_serialized], $tdata->{public}, 'public key ok');
		}
	};

	++$case_num;
}

$case_num = 0;
for my $tdata (@test_data_public) {
	subtest "testing public data, case $case_num" => sub {
		my $base_key = Bitcoin::Crypto::Key::ExtPublic->from_serialized([base58 => $tdata->{parent}]);
		my $key = $base_key->derive_key($tdata->{path});
		is(to_format [base58 => $key->to_serialized], $tdata->{child}, 'key derivation ok');
	};

	++$case_num;
}

$case_num = 0;
for my $tdata (@test_data_error) {
	subtest "testing invalid data, case $case_num" => sub {
		isa_ok dies {
			my $base_key = Bitcoin::Crypto::Key::ExtPublic->from_serialized([base58 => $tdata->[0]]);
			$base_key->derive_key($tdata->[1]);
		}, 'Bitcoin::Crypto::Exception';
	};

	++$case_num;
}

subtest 'should derive keys by using DerivationPath object directly' => sub {
	my $parent = $test_data_public[0]{parent};
	my $path = Bitcoin::Crypto::DerivationPath->new(
		private => 0,
		path => [],
	);

	my $base_key = Bitcoin::Crypto::Key::ExtPublic->from_serialized([base58 => $parent]);
	my $key = $base_key->derive_key($path);
	is(to_format [base58 => $key->to_serialized], $parent, 'derived ok');
};

done_testing;

