import {
  estimateTxVBytesSimple,
  estimateTxFee,
} from "../src/calculators/baseTx";
import { expect } from "chai";
import { ScriptType } from "../src/types";

describe("Real Transactions", () => {
  let tolerancePct = 0.01; // 0.1%
  it("should match 1 P2TR - 1 P2TR spend", () => {
    // https://mempool.space/signet/tx/d6610a3b03d8e460d0e8b1235413a9690136bc6603cfceaf42f33b5959ef046d
    const txInfo = {
      inputType: [ScriptType.P2TR],
      outputType: [ScriptType.P2TR],
      vsize: 111,
      fee: 407,
      feeRate: 3.67,
    };

    const vBytes = estimateTxVBytesSimple(txInfo.inputType, txInfo.outputType);
    let tolerance = Math.ceil(txInfo.vsize * tolerancePct);
    expect(vBytes).closeTo(txInfo.vsize, tolerance, "Size mismatch");
    expect(vBytes).gte(txInfo.vsize, "Size lower than expected");

    const calculatedFee = estimateTxFee(
      txInfo.inputType,
      txInfo.outputType,
      txInfo.feeRate,
    );
    tolerance = Math.ceil(txInfo.fee * tolerancePct);
    expect(calculatedFee).closeTo(txInfo.fee, tolerance, "Fee mismatch");
    expect(calculatedFee).gte(txInfo.fee, "Fee lower than expected");

    const feeRate = calculatedFee / vBytes;
    tolerance = Math.ceil(txInfo.feeRate * tolerancePct);
    expect(feeRate).closeTo(txInfo.feeRate, tolerance, "Fee rate mismatch");
    expect(feeRate).gte(txInfo.feeRate, "Fee rate lower than expected");
  });

  it("should match 3 P2WPKH - (25 P2TR - 1 P2WPKH) spend", () => {
    // https://mempool.space/signet/tx/bf7efd062c99c7ce500e0b706134344e22a485620bf6df80999d97f7bdf71c58
    const inputType = Array(3).fill(ScriptType.P2WPKH);
    const outputType = Array(25).fill(ScriptType.P2TR);
    outputType.push(ScriptType.P2WPKH);
    const txInfo = {
      inputType,
      outputType,
      vsize: 1_320,
      fee: 1_320,
      feeRate: 1,
    };

    const vBytes = estimateTxVBytesSimple(txInfo.inputType, txInfo.outputType);
    let tolerance = Math.ceil(txInfo.vsize * tolerancePct);
    expect(vBytes).closeTo(txInfo.vsize, tolerance, "Size mismatch");
    expect(vBytes).gte(txInfo.vsize, "Size lower than expected");

    const calculatedFee = estimateTxFee(
      txInfo.inputType,
      txInfo.outputType,
      txInfo.feeRate,
    );
    tolerance = Math.ceil(txInfo.fee * tolerancePct);
    expect(calculatedFee).closeTo(txInfo.fee, tolerance, "Fee mismatch");
    expect(calculatedFee).gte(txInfo.fee, "Fee lower than expected");

    const feeRate = calculatedFee / vBytes;
    tolerance = Math.ceil(txInfo.feeRate * tolerancePct);
    expect(feeRate).closeTo(txInfo.feeRate, tolerance, "Fee rate mismatch");
    expect(feeRate).gte(txInfo.feeRate, "Fee rate lower than expected");
  });

  it("should match 1 P2PKH - (12 P2WPKH - 2 P2SH) spend", () => {
    tolerancePct = 0.005;
    // https://mempool.space/tx/e5b238731246678d6df8f38e0ab0c72093fb9df02fc4f326f80be15975dbecfa
    const inputType = Array(1).fill(ScriptType.P2WPKH);
    const outputType = Array(12).fill(ScriptType.P2WPKH);
    outputType.push(ScriptType.P2SH, ScriptType.P2SH);
    const txInfo = {
      inputType,
      outputType,
      vsize: 514.25,
      fee: 2_060,
      feeRate: 4.01,
    };

    const vBytes = estimateTxVBytesSimple(txInfo.inputType, txInfo.outputType);

    let tolerance = Math.ceil(txInfo.vsize * tolerancePct);
    expect(vBytes).closeTo(txInfo.vsize, tolerance, "Size mismatch");
    expect(vBytes).gte(txInfo.vsize, "Size lower than expected");

    const calculatedFee = estimateTxFee(
      txInfo.inputType,
      txInfo.outputType,
      txInfo.feeRate,
    );
    tolerance = Math.ceil(txInfo.fee * tolerancePct);
    expect(calculatedFee).closeTo(txInfo.fee, tolerance, "Fee mismatch");
    expect(calculatedFee).gte(txInfo.fee, "Fee lower than expected");

    const feeRate = calculatedFee / vBytes;
    tolerance = Math.ceil(txInfo.feeRate * tolerancePct);
    expect(feeRate).closeTo(txInfo.feeRate, tolerance, "Fee rate mismatch");
    expect(feeRate).gte(txInfo.feeRate, "Fee rate lower than expected");
  });

  it("should match 1 P2TR - 8 P2TR spend", () => {
    const inputType = Array(1).fill(ScriptType.P2TR);
    const outputType = Array(8).fill(ScriptType.P2TR);
    const txInfo = {
      inputType,
      outputType,
      vsize: 412,
      fee: 4_078,
      feeRate: 9.9,
    };

    const vBytes = estimateTxVBytesSimple(txInfo.inputType, txInfo.outputType);

    let tolerance = Math.ceil(txInfo.vsize * tolerancePct);
    expect(vBytes).closeTo(txInfo.vsize, tolerance, "Size mismatch");
    expect(vBytes).gte(txInfo.vsize, "Size lower than expected");

    const calculatedFee = estimateTxFee(
      txInfo.inputType,
      txInfo.outputType,
      txInfo.feeRate,
    );
    tolerance = Math.ceil(txInfo.fee * tolerancePct);
    expect(calculatedFee).closeTo(txInfo.fee, tolerance, "Fee mismatch");
    expect(calculatedFee).gte(txInfo.fee, "Fee lower than expected");

    const feeRate = calculatedFee / vBytes;
    tolerance = Math.ceil(txInfo.feeRate * tolerancePct);
    expect(feeRate).closeTo(txInfo.feeRate, tolerance, "Fee rate mismatch");
    expect(feeRate).gte(txInfo.feeRate, "Fee rate lower than expected");
  });

  it("should match 1 P2WPKH - (1 P2SH - 1 P2WPKH) spend", () => {
    // https://mempool.space/tx/0875f8f0114183269c7c13a2392ae3bb3df30a4525a49b89de0751970a439469
    const inputType = Array(1).fill(ScriptType.P2WPKH);
    const outputType = [ScriptType.P2SH, ScriptType.P2WPKH];
    const txInfo = {
      inputType,
      outputType,
      vsize: 141.25,
      fee: 2_840,
      feeRate: 20.1,
    };

    const vBytes = estimateTxVBytesSimple(txInfo.inputType, txInfo.outputType);

    let tolerance = Math.ceil(txInfo.vsize * tolerancePct);
    expect(vBytes).closeTo(txInfo.vsize, tolerance, "Size mismatch");
    expect(vBytes).gte(txInfo.vsize, "Size lower than expected");

    const calculatedFee = estimateTxFee(
      txInfo.inputType,
      txInfo.outputType,
      txInfo.feeRate,
    );
    tolerance = Math.ceil(txInfo.fee * tolerancePct);
    expect(calculatedFee).closeTo(txInfo.fee, tolerance, "Fee mismatch");
    expect(calculatedFee).gte(txInfo.fee, "Fee lower than expected");

    const feeRate = calculatedFee / vBytes;
    tolerance = Math.ceil(txInfo.feeRate * tolerancePct);
    expect(feeRate).closeTo(txInfo.feeRate, tolerance, "Fee rate mismatch");
    expect(feeRate).gte(txInfo.feeRate, "Fee rate lower than expected");
  });

  it("should match 1 P2SH-P2WPKH - (8 P2WPKH - 2 P2SH - 2 P2PKH) spend", () => {
    // https://mempool.space/tx/cb7f5859d57998be3e4f236a6059ca6c3c68d12229da4cc527b51e794a8598f8
    const inputType = Array(1).fill(ScriptType.P2SH_P2WPKH);
    const outputType = Array(8).fill(ScriptType.P2WPKH);
    outputType.push(
      ScriptType.P2SH,
      ScriptType.P2SH,
      ScriptType.P2PKH,
      ScriptType.P2PKH,
    );
    const txInfo = {
      inputType,
      outputType,
      vsize: 481.25,
      fee: 539,
      feeRate: 1.12,
    };

    const vBytes = estimateTxVBytesSimple(txInfo.inputType, txInfo.outputType);

    let tolerance = Math.ceil(txInfo.vsize * tolerancePct);
    expect(vBytes).closeTo(txInfo.vsize, tolerance, "Size mismatch");
    expect(vBytes).gte(txInfo.vsize, "Size lower than expected");

    const calculatedFee = estimateTxFee(
      txInfo.inputType,
      txInfo.outputType,
      txInfo.feeRate,
    );
    tolerance = Math.ceil(txInfo.fee * tolerancePct);
    expect(calculatedFee).closeTo(txInfo.fee, tolerance, "Fee mismatch");
    expect(calculatedFee).gte(txInfo.fee, "Fee lower than expected");

    const feeRate = calculatedFee / vBytes;
    tolerance = Math.ceil(txInfo.feeRate * tolerancePct);
    expect(feeRate).closeTo(txInfo.feeRate, tolerance, "Fee rate mismatch");
    expect(feeRate).gte(txInfo.feeRate, "Fee rate lower than expected");
  });

  it("should match 1 P2SH-P2WPKH - (1 OP_RETURN - 2 P2WPKH - 1 P2SH) spend", () => {
    // https://mempool.space/tx/95c8d605c43eebb0ca7f2d9bce8f02ad5348f9952b5615418f41e2135a21aef7
    const inputType = Array(1).fill(ScriptType.P2SH_P2WPKH);
    const outputType = Array(2).fill(ScriptType.P2WPKH);
    outputType.push(ScriptType.P2SH);
    const txInfo = {
      inputType,
      outputType,
      vsize: 246,
      fee: 247_662,
      feeRate: 1_006,
      op_return: Buffer.from(
        "2098acf9ea96b1672479e27daa495df8dbeea5ea3ff16f5ed0e8d814b189a6ff8d00000000000000",
        "hex",
      ),
    };

    const vBytes = estimateTxVBytesSimple(
      txInfo.inputType,
      txInfo.outputType,
      txInfo.op_return,
    );

    let tolerance = Math.ceil(txInfo.vsize * tolerancePct);
    expect(vBytes).closeTo(txInfo.vsize, tolerance, "Size mismatch");
    expect(vBytes).gte(txInfo.vsize, "Size lower than expected");

    const calculatedFee = estimateTxFee(
      txInfo.inputType,
      txInfo.outputType,
      txInfo.feeRate,
      txInfo.op_return,
    );
    tolerance = Math.ceil(txInfo.fee * tolerancePct);
    expect(calculatedFee).closeTo(txInfo.fee, tolerance, "Fee mismatch");

    const feeRate = calculatedFee / vBytes;
    tolerance = Math.ceil(txInfo.feeRate * tolerancePct);
    expect(feeRate).closeTo(txInfo.feeRate, tolerance, "Fee rate mismatch");
    expect(feeRate).gte(txInfo.feeRate, "Fee rate lower than expected");
  });
});
