from decimal import Decimal
import time

from test_framework.bch.script import anycanspend_locking_script

from .nodemessages import CBlock, CTransaction, CTxIn, COutPoint, CTxOut
from ..script import CScript
from ..constants import COIN
from ..serialize import ser_string, serialize_script_num

COINBASE_REWARD = Decimal("50.00000000")


def create_block(hashprev, coinbase, n_time: int | None = None, txns=None) -> CBlock:
    """
    Create a block (with regtest difficulty)
    """
    block = CBlock()
    if n_time is None:
        block.n_time = int(time.time() + 600)
    else:
        block.n_time = n_time
    if isinstance(hashprev, str):
        hashprev = int(hashprev, 16)
    block.hash_prev_block = hashprev
    block.n_bits = 0x207FFFFF  # Will break after a difficulty adjustment...
    if coinbase:
        block.vtx.append(coinbase)
    if txns:
        txns.sort(key=lambda x: x.hash)
        block.vtx += txns
    block.hash_merkle_root = block.calc_merkle_root()
    block.rehash()
    return block


def create_coinbase(height) -> CTransaction:
    """
    Create a coinbase transaction, assuming no miner fees.
    If pubkey is passed in, the coinbase output will be a P2PK output;
    otherwise an anyone-can-spend output.
    """

    coinbase = CTransaction()
    coinbase.vin.append(
        CTxIn(
            COutPoint(0, 0xFFFFFFFF),
            ser_string(serialize_script_num(height)),
            0xFFFFFFFF,
        )
    )
    coinbaseoutput = CTxOut()
    coinbaseoutput.n_value = int(COINBASE_REWARD) * COIN
    halvings = int(height / 150)  # regtest
    coinbaseoutput.n_value >>= halvings

    script_pubkey = anycanspend_locking_script()
    coinbaseoutput.script_pubkey = CScript(script_pubkey)
    coinbase.vout = [coinbaseoutput]

    # Make sure the coinbase is at least 100 bytes
    coinbase_size = len(coinbase.serialize())
    if coinbase_size < 100:
        coinbase.vin[0].script_sig += b"x" * (100 - coinbase_size)

    coinbase.calc_sha256()
    return coinbase


PADDED_ANY_SPEND = (
    b"\x61" * 50
)  # add a bunch of OP_NOPs to make sure this tx is long enough


# out_type not used on BCH; but accepted paramter for compatibility with nex
# pylint: disable=too-many-positional-arguments,too-many-arguments,unused-argument
def create_transaction(
    prevtx: CTransaction, n, sig, value, out=PADDED_ANY_SPEND, out_type=0
) -> CTransaction:
    """
    Create a transaction with an anyone-can-spend output, that spends the
    nth output of prevtx.  pass a single integer value to make one output,
    or a list to create multiple outputs
    """
    prevtx.calc_sha256()
    if not isinstance(value, list):
        value = [value]
    tx = CTransaction()
    assert n < len(prevtx.vout)
    tx.vin.append(CTxIn(COutPoint(prevtx.sha256, n), sig, 0xFFFFFFFF))
    for v in value:
        tx.vout.append(CTxOut(v, out))
    tx.rehash()
    return tx
