#! /usr/bin/env python
'''Test of SNICKER functionality using Joinmarket
   wallets as defined in jmclient.wallet.'''

import pytest

from commontest import make_wallets, dummy_accept_callback, dummy_info_callback
import jmbitcoin as btc
from jmbase import get_log, bintohex
from jmclient import (load_test_config, estimate_tx_fee, SNICKERReceiver,
                      direct_send, BaseWallet)

pytestmark = pytest.mark.usefixtures("setup_regtest_bitcoind")

log = get_log()

@pytest.mark.parametrize(
    "nw, wallet_structures, mean_amt, sdev_amt, amt, net_transfer", [
        (2, [[1, 0, 0, 0, 0]] * 2, 4, 0, 20000000, 1000),
    ])
def test_snicker_e2e(setup_snicker, nw, wallet_structures,
                     mean_amt, sdev_amt, amt, net_transfer):
    """ Test strategy:
    1. create two wallets.
    2. with wallet 1 (Receiver), create a single transaction
    tx1, from mixdepth 0 to 1.
    3. with wallet 2 (Proposer), take pubkey of all inputs from tx1, and use
    them to create snicker proposals to the non-change out of tx1,
    in base64 and place in proposals.txt.
    4. Receiver polls for proposals in the file manually (instead of twisted
    LoopingCall) and processes them.
    5. Check for valid final transaction with broadcast.
    """

    # TODO: Make this test work with native segwit wallets
    wallets = make_wallets(nw, wallet_structures, mean_amt, sdev_amt)
    for w in wallets.values():
        w['wallet'].sync_wallet(fast=True)
    print(wallets)
    wallet_r = wallets[0]['wallet']
    wallet_p = wallets[1]['wallet']
    # next, create a tx from the receiver wallet
    our_destn_script = wallet_r.get_new_script(1, BaseWallet.ADDRESS_TYPE_INTERNAL)
    tx = direct_send(wallet_r, 0,
                     [(
                        wallet_r.script_to_addr(our_destn_script),
                        btc.coins_to_satoshi(0.3)
                     )],
                     accept_callback=dummy_accept_callback,
                     info_callback=dummy_info_callback,
                     return_transaction=True)    
    
    assert tx, "Failed to spend from receiver wallet"
    print("Parent transaction OK. It was: ")
    print(btc.human_readable_transaction(tx))
    wallet_r.process_new_tx(tx)
    # we must identify the receiver's output we're going to use;
    # it can be destination or change, that's up to the proposer
    # to guess successfully; here we'll just choose index 0.
    txid1 = tx.GetTxid()[::-1]
    txid1_index = 0

    receiver_start_bal = sum([x['value'] for x in wallet_r.get_all_utxos(
        ).values()])

    # Now create a proposal for every input index in tx1
    # (version 1 proposals mean we source keys from the/an
    # ancestor transaction)
    propose_keys = []
    for i in range(len(tx.vin)):
        # todo check access to pubkey
        sig, pub = [a for a in iter(tx.wit.vtxinwit[i].scriptWitness)]
        propose_keys.append(pub)
    # the proposer wallet needs to choose a single
    # utxo that is bigger than the output amount of tx1
    prop_m_utxos = wallet_p.get_utxos_by_mixdepth()[0]
    prop_utxo = prop_m_utxos[list(prop_m_utxos)[0]]
    # get the private key for that utxo
    priv = wallet_p.get_key_from_addr(
        wallet_p.script_to_addr(prop_utxo['script']))
    prop_input_amt = prop_utxo['value']
    # construct the arguments for the snicker proposal:
    our_input = list(prop_m_utxos)[0] # should be (txid, index)
    their_input = (txid1, txid1_index)
    our_input_utxo = btc.CMutableTxOut(prop_utxo['value'],
                                       prop_utxo['script'])
    fee_est = estimate_tx_fee(len(tx.vin), 2,
                              txtype=wallet_p.get_txtype())
    change_spk = wallet_p.get_new_script(0, BaseWallet.ADDRESS_TYPE_INTERNAL)

    encrypted_proposals = []

    for p in propose_keys:
        # TODO: this can be a loop over all outputs,
        # not just one guessed output, if desired.
        encrypted_proposals.append(
            wallet_p.create_snicker_proposal(
            [our_input], their_input,
            [our_input_utxo],
            tx.vout[txid1_index],
            net_transfer,
            fee_est,
            priv,
            p,
            prop_utxo['script'],
            change_spk,
            version_byte=1) + b"," + bintohex(p).encode('utf-8'))
    sR = SNICKERReceiver(wallet_r)
    sR.process_proposals([x.decode("utf-8") for x in encrypted_proposals])
    assert len(sR.successful_txs) == 1
    wallet_r.process_new_tx(sR.successful_txs[0])
    end_utxos = wallet_r.get_all_utxos()
    print("At end the receiver has these utxos: ", end_utxos)
    receiver_end_bal = sum([x['value'] for x in end_utxos.values()])
    assert receiver_end_bal == receiver_start_bal + net_transfer

@pytest.fixture(scope="module")
def setup_snicker(request):
    load_test_config()
