#!/usr/bin/env python3
# Copyright (c) 2024 The Bitcoin Unlimited developers
import asyncio
from test_framework.connectrum.exc import ElectrumErrorResponse
from test_framework.constants import COIN
from test_framework.environment import on_bch, on_nex
from test_framework.util import assert_equal
from test_framework.electrumutil import (
    ERROR_CODE_INVALID_PARAMS,
    ElectrumTestFramework,
)
from test_framework.electrumconnection import ElectrumConnection


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

        cli = ElectrumConnection()
        try:
            await self.bootstrap_p2p()
            await cli.connect()
            await self.test_invalid_params(cli)

            n.generate(110)

            # Add some txs to one of the blocks
            addr = n.getnewaddress()
            for _ in range(50):
                n.sendtoaddress(addr, 100_000_000 / COIN)
            n.generate(1)

            tip_height = n.getblockcount()
            assert_equal(tip_height, 111)
            await self.sync_height(cli)

            await self.test_basic(n, cli, tip_height)
            await self.test_by_blockhash(n, cli)

        finally:
            cli.disconnect()

    async def test_basic(self, n, cli, tip_height):
        for i in range(0, tip_height + 1):
            expected_header = n.getblockheader(i)
            expected_header_hex = n.getblockheader(i, False)

            header = await cli.call("blockchain.block.header_verbose", i)

            assert_equal(header["hex"], expected_header_hex)
            assert_equal(header["hash"], expected_header["hash"])
            assert_equal(header["height"], expected_header["height"])
            assert_equal(header["merkleroot"], expected_header["merkleroot"])
            assert_equal(header["nonce"], expected_header["nonce"])
            if i > 0:
                assert_equal(
                    header["previousblockhash"],
                    expected_header["previousblockhash"],
                )
            assert_equal(header["mediantime"], expected_header["mediantime"])
            assert_equal(header["time"], expected_header["time"])

        if on_bch():
            assert_equal(header["version"], expected_header["version"])

        elif on_nex():
            assert_equal(header["ancestorhash"], expected_header["ancestorhash"])
            # nexad doesn't provide txfilter
            assert "txfilter" in header
            assert_equal(header["chainwork"], expected_header["chainwork"])
            assert_equal(header["size"], expected_header["size"])
            assert_equal(header["txcount"], expected_header["txcount"])
            assert_equal(header["feepoolamt"], expected_header["feePoolAmt"])
            assert_equal(header["utxocommitment"], expected_header["utxoCommitment"])

        else:
            raise NotImplementedError()

    async def test_by_blockhash(self, n, cli):
        tip_hash = n.getbestblockhash()
        header = await cli.call("blockchain.block.header_verbose", tip_hash)
        assert_equal(tip_hash, header["hash"])

    async def test_invalid_params(self, cli):
        # invalid height
        try:
            await cli.call("blockchain.block.header_verbose", 100_000)
        except ElectrumErrorResponse as e:
            assert_equal(ERROR_CODE_INVALID_PARAMS, e.response["code"])

        # invalid hash
        try:
            await cli.call(
                "blockchain.block.header_verbose",
                "0f00d0013cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206",
            )
        except ElectrumErrorResponse as e:
            assert_equal(ERROR_CODE_INVALID_PARAMS, e.response["code"])


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