//! Microbenchmark: cost of `bitcoin::Transaction::total_sigop_cost` on
//! real recent blocks.
//!
//! Strategy: pull a sample of recent blocks via RPC (already-decoded
//! `bitcoin::Block`), and for each tx, time `total_sigop_cost` twice:
//!
//! 1. `|_| None` lookup — counts only legacy script_sig / script_pubkey
//!    sigops (skips P2SH redeem + witness). Cheap lower bound.
//! 2. Synthetic prevout map seeded with a P2WSH-shaped script_pubkey for
//!    every input, forcing the witness sigop walk to fire on every input.
//!    Pessimistic upper bound.
//!
//! The realistic cost is between these two, weighted by how many inputs
//! are actually P2SH/witness (~95% on mainnet today).
//!
//! Sample = N most recent blocks via `getblock` (verbosity 0 = raw bytes,
//! decoded by the iterator).

use std::time::Instant;

use bitcoin::{OutPoint, ScriptBuf, TxOut};
use brk_error::Result;
use brk_iterator::Blocks;
use brk_reader::Reader;
use brk_rpc::{Auth, Client};
use brk_types::Height;

fn main() -> Result<()> {
    let bitcoin_dir = Client::default_bitcoin_path();
    let client = Client::new(
        Client::default_url(),
        Auth::CookieFile(bitcoin_dir.join(".cookie")),
    )?;
    let reader = Reader::new(bitcoin_dir.join("blocks"), &client);
    let blocks = Blocks::new(&client, &reader);

    let tip: u32 = client.get_block_count()? as u32;
    const SAMPLE_BLOCKS: u32 = 16;
    let start = Height::new(tip - SAMPLE_BLOCKS);
    let end = Height::new(tip);

    println!(
        "Sampling blocks {}..{} ({} blocks)",
        u32::from(start),
        u32::from(end),
        SAMPLE_BLOCKS
    );

    let mut all_txs: Vec<bitcoin::Transaction> = Vec::with_capacity(64_000);
    let mut total_inputs: usize = 0;
    let mut total_outputs: usize = 0;
    let mut total_witness_bytes: usize = 0;
    let mut total_script_sig_bytes: usize = 0;

    let t_fetch = Instant::now();
    for block in blocks.range(start, end)? {
        let block = block?;
        for tx in &block.txdata {
            total_inputs += tx.input.len();
            total_outputs += tx.output.len();
            for input in &tx.input {
                total_script_sig_bytes += input.script_sig.len();
                total_witness_bytes += input.witness.iter().map(|w| w.len()).sum::<usize>();
            }
            all_txs.push(tx.clone());
        }
    }
    let t_fetch = t_fetch.elapsed();

    let n = all_txs.len();
    println!(
        "Fetched {n} txs in {:?}: {} inputs, {} outputs, \
         scriptSig={} bytes, witness={} bytes",
        t_fetch, total_inputs, total_outputs, total_script_sig_bytes, total_witness_bytes
    );

    // 1) Cheap lower bound: |_| None lookup.
    let t1 = Instant::now();
    let mut sum_low: u64 = 0;
    for tx in &all_txs {
        sum_low += tx.total_sigop_cost(|_| None) as u64;
    }
    let elapsed_low = t1.elapsed();
    println!(
        "[None lookup ] {n} txs in {:?} = {:.0} ns/tx, sum sigops={}",
        elapsed_low,
        elapsed_low.as_nanos() as f64 / n as f64,
        sum_low
    );

    // 2) Pessimistic upper bound: P2WSH-shaped prevout for every input,
    // forcing the full witness walk. Use a 32-byte zero hash; the witness
    // last element will be empty so witness sigop count is 0, but the
    // is_p2wsh path runs end-to-end.
    let p2wsh_spk = {
        let mut bytes = vec![0x00, 0x20];
        bytes.extend_from_slice(&[0u8; 32]);
        ScriptBuf::from_bytes(bytes)
    };
    let synthetic_txout = TxOut {
        value: bitcoin::Amount::from_sat(0),
        script_pubkey: p2wsh_spk,
    };

    let t2 = Instant::now();
    let mut sum_hi: u64 = 0;
    for tx in &all_txs {
        sum_hi += tx.total_sigop_cost(|_op: &OutPoint| Some(synthetic_txout.clone())) as u64;
    }
    let elapsed_hi = t2.elapsed();
    println!(
        "[P2WSH lookup] {n} txs in {:?} = {:.0} ns/tx, sum sigops={}",
        elapsed_hi,
        elapsed_hi.as_nanos() as f64 / n as f64,
        sum_hi
    );

    // 3) Block-level extrapolation. Mainnet averages ~3000 tx/block, so
    // per-block cost ~= ns/tx * 3000.
    let txs_per_block = (n / SAMPLE_BLOCKS as usize) as f64;
    let block_low_us = elapsed_low.as_nanos() as f64 / SAMPLE_BLOCKS as f64 / 1000.0;
    let block_hi_us = elapsed_hi.as_nanos() as f64 / SAMPLE_BLOCKS as f64 / 1000.0;
    println!(
        "Per-block (avg {:.0} tx): low={:.1} us, high={:.1} us",
        txs_per_block, block_low_us, block_hi_us
    );

    Ok(())
}
