#!/usr/bin/env python3
# Copyright (c) 2020 The Bitcoin Unlimited developers
"""
Tests specific for the electrum call 'blockchain.scripthash.get_history'
"""

import asyncio
from test_framework.environment import on_bch, on_nex

from test_framework.utilcompat import lock

from test_framework.util import assert_equal
from test_framework.electrumutil import (
    ElectrumTestFramework,
    script_to_scripthash,
    get_txid_from_idem,
)
from test_framework.script import CScript, OP_TRUE, OP_DROP, OP_NOP
from test_framework.utiltx import pad_tx
from test_framework.electrumconnection import ElectrumConnection

if on_bch():
    from test_framework.bch.script import anyonecanspend_unlocking_script
elif on_nex():
    from test_framework.nex.script import anyonecanspend_unlocking_script
else:
    raise NotImplementedError()
GET_HISTORY = "blockchain.scripthash.get_history"
ADDRESS_GET_HISTORY = "blockchain.address.get_history"


class ElectrumScripthashGetHistory(ElectrumTestFramework):
    async def run_test(self):
        n = self.nodes[0]

        await self.bootstrap_p2p()
        cli = ElectrumConnection()
        await cli.connect()
        coinbases = await self.mine_blocks(cli, n, 100)
        try:
            await self.test_blockheight_confirmed(n, cli, coinbases.pop(0))
            await self.test_tokens_in_history(n, cli)
        finally:
            cli.disconnect()

    async def test_blockheight_confirmed(self, n, cli, unspent):
        # Just a unique anyone-can-spend scriptpubkey
        scriptpubkey = CScript([OP_TRUE, OP_DROP, OP_NOP])
        scripthash = script_to_scripthash(lock(scriptpubkey))

        # There should exist any history for scripthash
        assert_equal(0, len(await cli.call(GET_HISTORY, scripthash)))

        # Send tx to scripthash and confirm it
        tx = self.create_transaction(
            unspent,
            n=0,
            value=unspent.vout[0].n_value,
            sig=anyonecanspend_unlocking_script(),
            out=lock(scriptpubkey),
        )
        pad_tx(tx)

        await self.mine_blocks(cli, n, 1, txns=[tx])

        # History should now have 1 entry at current tip height
        res = await cli.call(GET_HISTORY, scripthash)
        assert_equal(1, len(res))
        assert_equal(n.getblockcount(), res[0]["height"])
        assert_equal(tx.get_rpc_hex_id(), res[0]["tx_hash"])

    async def test_tokens_in_history(self, n, cli):
        """
        Even though token amounts are added to outputs
        scriptpubkey (and would change the script hash),
        they should still be found by address query.
        """
        if on_bch():
            self.info("Skipping test_tokens_in_history on BCH")
            return

        # Node needs coins for fees
        n.generate(101)

        # Create and send tokens

        addr1 = n.getnewaddress()
        addr2 = n.getnewaddress()

        token = n.token("new")
        group_id = token["groupIdentifier"]
        txidem_mint = n.token("mint", group_id, addr1, 42)
        txidem_send = n.token("send", group_id, addr1, 42)
        txidem_send2 = n.token("send", group_id, addr2, 42)

        for txidem in [txidem_mint, txidem_send, txidem_send2]:
            await self.wait_for_mempool_tx(cli, await get_txid_from_idem(n, txidem))

        def has_tx(res, txhash):
            for tx in res:
                if tx["tx_hash"] == txhash:
                    return True
            return False

        # addr1 should have all 3 transactions in its history.
        res = await cli.call(ADDRESS_GET_HISTORY, addr1)
        assert_equal(3, len(res))
        assert has_tx(res, await get_txid_from_idem(n, txidem_mint))
        assert has_tx(res, await get_txid_from_idem(n, txidem_send))
        assert has_tx(res, await get_txid_from_idem(n, txidem_send2))

        # addr2 should have the last send in its history
        res = await cli.call(ADDRESS_GET_HISTORY, addr2)
        assert_equal(1, len(res))
        assert has_tx(res, await get_txid_from_idem(n, txidem_send2))


if __name__ == "__main__":
    asyncio.run(ElectrumScripthashGetHistory().main())
