//! Regression tests for _most_ types that implement `serde::Serialize`.
//!
//! For remaining types see: ./serde_opcodes.rs
//!
//! If you find a type defined in `rust-bitcoin` that implements `Serialize` and does _not_ have a
//! regression test please add it.
//!
//! Types/tests were found using, and are ordered by, the output of: `git grep -l Serialize`.
//!

// In tests below `deserialize` is consensus deserialize while `serialize` is serde serialize, that
// is why we have two different serialized data files for tests that use binary serialized input.
//
// To create a file with the expected serialized data do something like:
//
//  use std::fs::File;
//  use std::io::Write;
//  let script = ScriptBuf::from(vec![0u8, 1u8, 2u8]);
//  let got = serialize(&script).unwrap();
//  let mut file = File::create("/tmp/script_bincode").unwrap();
//  file.write_all(&got).unwrap();

#![cfg(feature = "serde")]

use std::collections::BTreeMap;
use std::str::FromStr;

use bincode::serialize;
use bitcoin::bip32::{ChildNumber, KeySource, Xpriv, Xpub};
use bitcoin::blockdata::locktime::{absolute, relative};
use bitcoin::blockdata::witness::Witness;
use bitcoin::consensus::encode::deserialize;
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
use bitcoin::hex::FromHex;
use bitcoin::psbt::raw::{self, Key, Pair, ProprietaryKey};
use bitcoin::psbt::{Input, Output, Psbt, PsbtSighashType};
use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
use bitcoin::taproot::{self, ControlBlock, LeafVersion, TapTree, TaprootBuilder};
use bitcoin::{
    ecdsa, transaction, Address, Amount, Block, NetworkKind, OutPoint, PrivateKey, PublicKey,
    ScriptBuf, Sequence, Target, Transaction, TxIn, TxOut, Txid, Work,
};

/// Implicitly does regression test for `BlockHeader` also.
#[test]
fn serde_regression_block() {
    let segwit = include_bytes!(
        "data/testnet_block_000000000000045e0b1660b6445b5e5c5ab63c9a4f956be7e1e69be04fa4497b.raw"
    );
    let block: Block = deserialize(segwit).unwrap();
    let got = serialize(&block).unwrap();
    let want = include_bytes!("data/serde/block_bincode");
    assert_eq!(got, want)
}

#[test]
fn serde_regression_absolute_lock_time_height() {
    let t = absolute::LockTime::from_height(741521).expect("valid height");
    let got = serialize(&t).unwrap();
    let want = include_bytes!("data/serde/absolute_lock_time_blocks_bincode") as &[_];
    assert_eq!(got, want);
}

#[test]
fn serde_regression_absolute_lock_time_time() {
    let seconds: u32 = 1653195600; // May 22nd, 5am UTC.
    let t = absolute::LockTime::from_time(seconds).expect("valid time");
    let got = serialize(&t).unwrap();

    let want = include_bytes!("data/serde/absolute_lock_time_seconds_bincode") as &[_];
    assert_eq!(got, want);
}

#[test]
fn serde_regression_relative_lock_time_height() {
    let t = relative::LockTime::from(relative::Height::from(0xCAFE_u16));
    let got = serialize(&t).unwrap();

    let want = include_bytes!("data/serde/relative_lock_time_blocks_bincode") as &[_];
    assert_eq!(got, want);
}

#[test]
fn serde_regression_relative_lock_time_time() {
    let t = relative::LockTime::from(relative::Time::from_512_second_intervals(0xFACE_u16));
    let got = serialize(&t).unwrap();

    let want = include_bytes!("data/serde/relative_lock_time_seconds_bincode") as &[_];
    assert_eq!(got, want);
}

#[test]
fn serde_regression_script() {
    let script = ScriptBuf::from(vec![0u8, 1u8, 2u8]);
    let got = serialize(&script).unwrap();
    let want = include_bytes!("data/serde/script_bincode") as &[_];
    assert_eq!(got, want)
}

#[test]
fn serde_regression_txin() {
    let ser = include_bytes!("data/serde/txin_ser");
    let txin: TxIn = deserialize(ser).unwrap();

    let got = serialize(&txin).unwrap();
    let want = include_bytes!("data/serde/txin_bincode") as &[_];
    assert_eq!(got, want)
}

#[test]
fn serde_regression_txout() {
    let txout = TxOut {
        value: Amount::from_sat(0xDEADBEEFCAFEBABE),
        script_pubkey: ScriptBuf::from(vec![0u8, 1u8, 2u8]),
    };
    let got = serialize(&txout).unwrap();
    let want = include_bytes!("data/serde/txout_bincode") as &[_];
    assert_eq!(got, want)
}

#[test]
fn serde_regression_transaction() {
    let ser = include_bytes!("data/serde/transaction_ser");
    let tx: Transaction = deserialize(ser).unwrap();
    let got = serialize(&tx).unwrap();
    let want = include_bytes!("data/serde/transaction_bincode") as &[_];
    assert_eq!(got, want)
}

#[test]
fn serde_regression_witness() {
    let w0 = Vec::from_hex("03d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f2105")
        .unwrap();
    let w1 = Vec::from_hex("000000").unwrap();
    let vec = vec![w0, w1];
    let witness = Witness::from_slice(&vec);

    let got = serialize(&witness).unwrap();
    let want = include_bytes!("data/serde/witness_bincode") as &[_];
    assert_eq!(got, want)
}

#[test]
fn serde_regression_address() {
    let s = include_str!("data/serde/public_key_hex");
    let pk = PublicKey::from_str(s.trim()).unwrap();
    let addr = Address::p2pkh(pk, NetworkKind::Main);

    let got = serialize(&addr).unwrap();
    let want = include_bytes!("data/serde/address_bincode") as &[_];
    assert_eq!(got, want)
}

#[test]
fn serde_regression_extended_priv_key() {
    let s = include_str!("data/serde/extended_priv_key");
    let key = Xpriv::from_str(s.trim()).unwrap();
    let got = serialize(&key).unwrap();
    let want = include_bytes!("data/serde/extended_priv_key_bincode") as &[_];
    assert_eq!(got, want)
}

#[test]
fn serde_regression_extended_pub_key() {
    let s = include_str!("data/serde/extended_pub_key");
    let key = Xpub::from_str(s.trim()).unwrap();
    let got = serialize(&key).unwrap();
    let want = include_bytes!("data/serde/extended_pub_key_bincode") as &[_];
    assert_eq!(got, want)
}

#[test]
fn serde_regression_ecdsa_sig() {
    let s = include_str!("data/serde/ecdsa_sig_hex");
    let sig = ecdsa::Signature {
        signature: secp256k1::ecdsa::Signature::from_str(s.trim()).unwrap(),
        sighash_type: EcdsaSighashType::All,
    };

    let got = serialize(&sig).unwrap();
    let want = include_bytes!("data/serde/ecdsa_sig_bincode") as &[_];
    assert_eq!(got, want)
}

#[test]
fn serde_regression_control_block() {
    let s = include_str!("data/serde/control_block_hex");
    let block = ControlBlock::decode(&Vec::<u8>::from_hex(s.trim()).unwrap()).unwrap();
    let got = serialize(&block).unwrap();

    let want = include_bytes!("data/serde/control_block_bincode") as &[_];
    assert_eq!(got, want)
}

#[test]
fn serde_regression_child_number() {
    let num = ChildNumber::Normal { index: 0xDEADBEEF };
    let got = serialize(&num).unwrap();
    let want = include_bytes!("data/serde/child_number_bincode") as &[_];
    assert_eq!(got, want)
}

#[test]
fn serde_regression_private_key() {
    let sk = PrivateKey::from_wif("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy").unwrap();
    let got = serialize(&sk).unwrap();
    let want = include_bytes!("data/serde/private_key_bincode") as &[_];
    assert_eq!(got, want)
}

#[test]
fn serde_regression_public_key() {
    let s = include_str!("data/serde/public_key_hex");
    let pk = PublicKey::from_str(s.trim()).unwrap();
    let got = serialize(&pk).unwrap();
    let want = include_bytes!("data/serde/public_key_bincode") as &[_];
    assert_eq!(got, want)
}

#[test]
fn serde_regression_psbt() {
    let tx = Transaction {
        version: transaction::Version::ONE,
        lock_time: absolute::LockTime::ZERO,
        input: vec![TxIn {
            previous_output: OutPoint {
                txid: "e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389"
                    .parse::<Txid>()
                    .unwrap(),
                vout: 1,
            },
            script_sig: ScriptBuf::from_hex("160014be18d152a9b012039daf3da7de4f53349eecb985")
                .unwrap(),
            sequence: Sequence::from_consensus(4294967295),
            witness: Witness::from_slice(&[Vec::from_hex(
                "03d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f2105",
            )
            .unwrap()]),
        }],
        output: vec![TxOut {
            value: Amount::from_sat(190_303_501_938),
            script_pubkey: ScriptBuf::from_hex("a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587")
                .unwrap(),
        }],
    };
    let unknown: BTreeMap<raw::Key, Vec<u8>> =
        vec![(raw::Key { type_value: 9, key: vec![0, 1] }, vec![3, 4, 5])].into_iter().collect();
    let key_source = ("deadbeef".parse().unwrap(), "0'/1".parse().unwrap());
    let keypaths: BTreeMap<secp256k1::PublicKey, KeySource> = vec![(
        "0339880dc92394b7355e3d0439fa283c31de7590812ea011c4245c0674a685e883".parse().unwrap(),
        key_source.clone(),
    )]
    .into_iter()
    .collect();

    let proprietary: BTreeMap<raw::ProprietaryKey, Vec<u8>> = vec![(
        raw::ProprietaryKey {
            prefix: "prefx".as_bytes().to_vec(),
            subtype: 42,
            key: "test_key".as_bytes().to_vec(),
        },
        vec![5, 6, 7],
    )]
    .into_iter()
    .collect();

    let psbt = Psbt {
        version: 0,
        xpub: {
            let s = include_str!("data/serde/extended_pub_key");
            let xpub = Xpub::from_str(s.trim()).unwrap();
            vec![(xpub, key_source)].into_iter().collect()
        },
        unsigned_tx: {
            let mut unsigned = tx.clone();
            unsigned.input[0].script_sig = ScriptBuf::new();
            unsigned.input[0].witness = Witness::default();
            unsigned
        },
        proprietary: proprietary.clone(),
        unknown: unknown.clone(),

        inputs: vec![Input {
            non_witness_utxo: Some(tx),
            witness_utxo: Some(TxOut {
                value: Amount::from_sat(190_303_501_938),
                script_pubkey: ScriptBuf::from_hex("a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587").unwrap(),
            }),
            sighash_type: Some(PsbtSighashType::from(EcdsaSighashType::from_str("SIGHASH_SINGLE|SIGHASH_ANYONECANPAY").unwrap())),
            redeem_script: Some(vec![0x51].into()),
            witness_script: None,
            partial_sigs: vec![(
                "0339880dc92394b7355e3d0439fa283c31de7590812ea011c4245c0674a685e883".parse().unwrap(),
                "304402204f67e2afb76142d44fae58a2495d33a3419daa26cd0db8d04f3452b63289ac0f022010762a9fb67e94cc5cad9026f6dc99ff7f070f4278d30fbc7d0c869dd38c7fe701".parse().unwrap(),
            )].into_iter().collect(),
            bip32_derivation: keypaths.clone().into_iter().collect(),
            final_script_witness: Some(Witness::from_slice(&[vec![1, 3], vec![5]])),
            ripemd160_preimages: vec![(ripemd160::Hash::hash(&[1, 2]), vec![1, 2])].into_iter().collect(),
            sha256_preimages: vec![(sha256::Hash::hash(&[1, 2]), vec![1, 2])].into_iter().collect(),
            hash160_preimages: vec![(hash160::Hash::hash(&[1, 2]), vec![1, 2])].into_iter().collect(),
            hash256_preimages: vec![(sha256d::Hash::hash(&[1, 2]), vec![1, 2])].into_iter().collect(),
            proprietary: proprietary.clone(),
            unknown: unknown.clone(),
            ..Default::default()
        }],
        outputs: vec![Output {
            bip32_derivation: keypaths.into_iter().collect(),
            proprietary,
            unknown,
            ..Default::default()
        }],
    };

    // Sanity, check we can roundtrip BIP-174 serialize.
    let serialized = psbt.serialize();
    Psbt::deserialize(&serialized).unwrap();

    let got = serialize(&psbt).unwrap();
    let want = include_bytes!("data/serde/psbt_bincode") as &[_];
    assert_eq!(got, want)
}

#[test]
fn serde_regression_raw_pair() {
    let pair = Pair {
        key: Key { type_value: 1u8, key: vec![0u8, 1u8, 2u8, 3u8] },
        value: vec![0u8, 1u8, 2u8, 3u8],
    };
    let got = serialize(&pair).unwrap();
    let want = include_bytes!("data/serde/raw_pair_bincode") as &[_];
    assert_eq!(got, want)
}

#[test]
fn serde_regression_proprietary_key() {
    let key = ProprietaryKey {
        prefix: vec![0u8, 1u8, 2u8, 3u8],
        subtype: 1u8,
        key: vec![0u8, 1u8, 2u8, 3u8],
    };
    let got = serialize(&key).unwrap();
    let want = include_bytes!("data/serde/proprietary_key_bincode") as &[_];
    assert_eq!(got, want)
}

#[test]
fn serde_regression_taproot_sig() {
    let s = include_str!("data/serde/taproot_sig_hex");
    let sig = taproot::Signature {
        signature: secp256k1::schnorr::Signature::from_str(s.trim()).unwrap(),
        sighash_type: TapSighashType::All,
    };

    let got = serialize(&sig).unwrap();
    let want = include_bytes!("data/serde/taproot_sig_bincode") as &[_];
    assert_eq!(got, want)
}

#[test]
fn serde_regression_taptree() {
    let ver = LeafVersion::from_consensus(0).unwrap();
    let script = ScriptBuf::from(vec![0u8, 1u8, 2u8]);
    let mut builder = TaprootBuilder::new().add_leaf_with_ver(1, script.clone(), ver).unwrap();
    builder = builder.add_leaf(1, script).unwrap();
    let tree = TapTree::try_from(builder).unwrap();

    let got = serialize(&tree).unwrap();
    let want = include_bytes!("data/serde/taptree_bincode") as &[_];
    assert_eq!(got, want)
}

// Used to get a 256 bit integer as a byte array.
fn le_bytes() -> [u8; 32] {
    let x: u128 = 0xDEAD_BEEF_CAFE_BABE_DEAD_BEEF_CAFE_BABE;
    let y: u128 = 0xCAFE_DEAD_BABE_BEEF_CAFE_DEAD_BABE_BEEF;

    let mut bytes = [0_u8; 32];

    bytes[..16].copy_from_slice(&x.to_le_bytes());
    bytes[16..].copy_from_slice(&y.to_le_bytes());

    bytes
}

#[test]
fn serde_regression_work() {
    let work = Work::from_le_bytes(le_bytes());
    let got = serialize(&work).unwrap();
    let want = include_bytes!("data/serde/u256_bincode") as &[_];
    assert_eq!(got, want)
}

#[test]
fn serde_regression_target() {
    let target = Target::from_le_bytes(le_bytes());
    let got = serialize(&target).unwrap();
    let want = include_bytes!("data/serde/u256_bincode") as &[_];
    assert_eq!(got, want)
}