// Package main demonstrates the bitcoin-go (BSV) library API.
//
// Run with:
//
//	go run ./examples
package main

import (
	"encoding/hex"
	"encoding/json"
	"fmt"

	bsv "gitcode.com/Youyi-Labs/bitcoin-go"
)

func must(err error) {
	if err != nil {
		panic(err)
	}
}

func main() {
	// ------------------------------------------------------------------
	// 1. Keys and addresses
	// ------------------------------------------------------------------
	fmt.Println("== 1. Keys and Addresses ==")
	priv, err := bsv.PrivateKeyFromWIF("cSBnVM4xvxarwGQuAfQFwqDg9k5tErHUHzgWsEfD4zdwUasvqRVY")
	must(err)
	fmt.Println("private key (WIF):", priv.ToString())
	fmt.Println("private key (hex):", priv.ToHex())

	pub := priv.ToPublicKey()
	fmt.Println("public key:", pub.ToString())
	fmt.Println("compressed:", pub.Compressed)

	addr, err := priv.ToAddress(bsv.Testnet)
	must(err)
	fmt.Println("address (testnet):", addr.ToString())
	fmt.Println("is P2PKH:", addr.IsPayToPublicKeyHash())

	// parse an address back
	addr2, err := bsv.AddressFromString("mszYqVnqKoQx4jcTdJXxwKAissE3Jbrrc1", nil, "")
	must(err)
	fmt.Println("parsed address:", addr2.ToString(), "network:", addr2.Network)

	// random key pair
	randomPriv, err := bsv.PrivateKeyFromRandom("testnet")
	must(err)
	fmt.Println("random key:", randomPriv.ToWIF())

	// ------------------------------------------------------------------
	// 2. HD wallet keys (BIP32)
	// ------------------------------------------------------------------
	fmt.Println("\n== 2. HD Keys (BIP32) ==")
	seed, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f")
	master, err := bsv.HDPrivateKeyFromSeed(seed, nil)
	must(err)
	fmt.Println("master xprv:", master.Xprivkey)
	fmt.Println("master xpub:", master.Xpubkey())

	child, err := master.DeriveChild("m/0'/1/2'", false)
	must(err)
	fmt.Println("child m/0'/1/2':", child.Xprivkey)

	// derive from the public key only (non-hardened)
	xpub, err := bsv.NewHDPublicKey(master.Xpubkey())
	must(err)
	grandchild, err := xpub.DeriveChild("m/0/1", false)
	must(err)
	fmt.Println("public m/0/1:", grandchild.Xpubkey)

	// ------------------------------------------------------------------
	// 3. Mnemonic (BIP39)
	// ------------------------------------------------------------------
	fmt.Println("\n== 3. Mnemonic (BIP39) ==")
	mnemonic, err := bsv.MnemonicFromRandom(nil)
	must(err)
	fmt.Println("random phrase:", mnemonic.ToString())

	// known phrase -> seed
	fixed, err := bsv.MnemonicFromString(
		"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", nil)
	must(err)
	seedBytes := fixed.ToSeed("TREZOR")
	fmt.Println("known seed:", hex.EncodeToString(seedBytes)[:32], "...")

	hdFromMnemonic, err := fixed.ToHDPrivateKey("", nil)
	must(err)
	fmt.Println("HD key from mnemonic:", hdFromMnemonic.Xprivkey[:20], "...")

	// ------------------------------------------------------------------
	// 4. Scripts
	// ------------------------------------------------------------------
	fmt.Println("\n== 4. Scripts ==")
	// build a P2PKH output script from an address
	p2pkh, err := bsv.BuildPublicKeyHashOut(addr)
	must(err)
	fmt.Println("P2PKH output:", p2pkh.ToASM())

	// OP_RETURN data outputs
	dataOut, err := bsv.BuildDataOut("hello bsv", "")
	must(err)
	fmt.Println("OP_RETURN:", dataOut.ToASM())
	safeOut, err := bsv.BuildSafeDataOut([]byte{1, 2, 3}, "")
	must(err)
	fmt.Println("safe OP_RETURN:", safeOut.ToASM())

	// parse a script from hex and classify it
	parsed, err := bsv.ScriptFromHex("76a914f4c03610e60ad15100929cc23da2f3a799af172588ac")
	must(err)
	fmt.Println("parsed script:", parsed.ToASM())
	fmt.Println("is P2PKH out:", parsed.IsPublicKeyHashOut())

	// derive the address a script pays to
	scriptAddr, err := parsed.ToAddress(bsv.Livenet)
	must(err)
	fmt.Println("script pays to:", scriptAddr.ToString())

	// multisig output
	k1, _ := bsv.PublicKeyFromHex("020483ebb834d91d494a3b649cf0e8f5c9c4fcec5f194ab94341cc99bb440007f2")
	k2, _ := bsv.PublicKeyFromHex("0271ebaeef1c2bf0c1a4772d1391eab03e4d96a6e9b48551ab4e4b0d2983eb452b")
	multisig, err := bsv.BuildMultisigOut([]*bsv.PublicKey{k1, k2}, 2, false)
	must(err)
	fmt.Println("2-of-2 multisig:", multisig.ToASM())

	// ------------------------------------------------------------------
	// 5. Build, sign and verify a transaction
	// ------------------------------------------------------------------
	fmt.Println("\n== 5. Transactions ==")
	// the UTXO we want to spend (a P2PKH output of our testnet address)
	fromAddr, err := priv.ToAddress(bsv.Testnet)
	must(err)
	scriptPubkey, err := bsv.BuildPublicKeyHashOut(fromAddr)
	must(err)
	utxo := map[string]interface{}{
		"txId":        "a477af6b2667c29670467e4e0728b685ee07b240235771862318e29ddbe58458",
		"outputIndex": 0,
		"script":      scriptPubkey,
		"satoshis":    100000,
	}

	tx, err := bsv.NewTransaction(nil)
	must(err)
	_, err = tx.From(utxo, nil, 0)
	must(err)
	_, err = tx.To("mrU9pEmAx26HcbKVrABvgL7AwA5fjNFoDc", 40000)
	must(err)
	_, err = tx.To("n28S35tqEMbt6vNad7A5K3mZ7vdn8dZ86X", 30000)
	must(err)
	_, err = tx.Sign(priv, 0)
	must(err)

	fmt.Println("fully signed:", tx.IsFullySigned())
	fmt.Println("txid:", tx.GetID())
	fmt.Println("serialized tx:", tx.ToHex())

	// verify the input's script with the interpreter (FORKID signatures need
	// the corresponding flag and the input amount)
	scriptSig := tx.Inputs[0].GetScript()
	verified, errstr := bsv.VerifyScript(scriptSig, scriptPubkey, tx, 0,
		bsv.SCRIPT_VERIFY_P2SH|bsv.SCRIPT_VERIFY_STRICTENC|bsv.SCRIPT_ENABLE_SIGHASH_FORKID,
		bsv.NewBNFromNumber(100000), nil)
	fmt.Println("script verified:", verified, "err:", errstr)

	// deserialize it back
	tx2, err := bsv.TransactionFromString(tx.ToHex())
	must(err)
	fmt.Println("roundtrip equal:", tx2.ToHex() == tx.ToHex())

	// BIP69 sorting and an OP_RETURN output
	tx.Sort()
	_, err = tx.AddData("genesis is coming")
	must(err)

	// ------------------------------------------------------------------
	// 6. Signed messages (BIP137)
	// ------------------------------------------------------------------
	fmt.Println("\n== 6. Signed Messages ==")
	msg, err := bsv.NewMessage("hello, world")
	must(err)
	sig, err := msg.Sign(priv)
	must(err)
	fmt.Println("signature:", sig)
	ok, err := msg.Verify(fromAddr.ToString(), sig)
	must(err)
	fmt.Println("verified:", ok)

	// ------------------------------------------------------------------
	// 7. ECIES encryption
	// ------------------------------------------------------------------
	fmt.Println("\n== 7. ECIES ==")
	bobPriv, err := bsv.PrivateKeyFromRandom(nil)
	must(err)
	// Alice encrypts to Bob
	enc := bsv.NewECIES(nil)
	enc.SetPrivateKey(priv)
	enc.SetPublicKey(bobPriv.ToPublicKey())
	ciphertext, err := enc.Encrypt("top secret", nil)
	must(err)
	fmt.Println("ciphertext:", hex.EncodeToString(ciphertext)[:32], "...")

	// Bob decrypts
	dec := bsv.NewECIES(nil)
	dec.SetPrivateKey(bobPriv)
	plaintext, err := dec.Decrypt(ciphertext)
	must(err)
	fmt.Println("decrypted:", string(plaintext))

	// ------------------------------------------------------------------
	// 8. Blocks, headers and merkle blocks
	// ------------------------------------------------------------------
	fmt.Println("\n== 8. Blocks ==")
	// genesis block header (first 80 bytes)
	headerHex := "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c"
	header, err := bsv.BlockHeaderFromString(headerHex)
	must(err)
	fmt.Println("genesis header hash:", header.Hash())

	// merkle block (BIP37): a filtered block with one matching transaction.
	// This is the official test vector exported from the JS test data.
	mbJSON := `{
		"header": {
			"hash": "0000000000000000011b04bc9f4f3856e299b53a335eb1c42be906237c860bb8",
			"version": 4,
			"prevHash": "0000000000000000015373947aa93c7cb16a308fb0a59644d4123072ad24ce5b",
			"merkleRoot": "ac1841eb3b7d380ee114270e3b1c7df349f1e27e2f0f7891138199bc07e006f8",
			"time": 1456274787,
			"bits": 403093919,
			"nonce": 73707849
		},
		"numTransactions": 3309,
		"hashes": [
			"adaf1e41a5349a7e2b27e6f2b5fc1186d576d21b47531a410a654439f49bd5a9",
			"0f64562b6d361757bfdc5926d28cafb7c45e4822b4ad5a14e65530d0c9d44cd2",
			"c34dec8d5954d8c151c3a594a5a6d1f3a1ec8a1a6c470b27aea879b7757328fb",
			"c044e5d998fe29cdbe88f87c97c3547f142f5b491cb909d9b0a8e9d3ab7fc984",
			"51ecd2d2b02b0ca1a3b439eae3adf87ab37dd87f7be0c5b3437cd22350079101",
			"684ebe37c6eaae2879249ff20b3769d5f2f7ad853a5363a4baee68c0c85777a5",
			"e561fbeb0faff42f95f2dc8d57d432d4d1a9f84483597a0602e1edc3390f1e33",
			"02c070afc743c885a6c8d94d15d6e9c3b991c636b4284258dd37686c8792fe44",
			"6f64fd5aa9dd01f74c03656d376625cf80328d83d9afebe60cc68b8f0e245bd9",
			"a296fef97b3da0825bac00c794ea234913421b7c1cbb8571978d8d5ba3b16de2",
			"691c6855c5da434a06cb39a7e47d9337da0e39112ad975cc70cecc882233920a",
			"6a9e7f8e6d1d8c326a774ff691bdf424252cee710f8e5d9da2c094e999c15efc",
			"0723ffc695fef989e86784fa2b47097d55f6aac244631536d8d901f5cd9bf170"
		],
		"flags": [171, 86, 23, 0]
	}`
	var mbObj map[string]interface{}
	must(json.Unmarshal([]byte(mbJSON), &mbObj))
	mb, err := bsv.NewMerkleBlock(mbObj)
	must(err)
	fmt.Println("merkle tree valid:", mb.ValidMerkleTree())
	filtered, err := mb.FilteredTxsHash()
	must(err)
	fmt.Println("matching txs:", filtered)

	// ------------------------------------------------------------------
	// 9. Encoding helpers
	// ------------------------------------------------------------------
	fmt.Println("\n== 9. Encoding ==")
	fmt.Println("base58(hello world):", bsv.Base58Encode([]byte("hello world")))
	fmt.Println("base58check of 33 bytes:", bsv.Base58CheckEncode(make([]byte, 33)))
}