import pytest

from eth.chains.base import MiningChain

from eth.tools.builder.chain import api


@pytest.fixture(params=api.mainnet_fork_at_fns)
def base_chain(request):
    chain = api.build(
        MiningChain,
        request.param(0),
        api.disable_pow_check(),
        api.genesis(),
    )

    return chain


@pytest.fixture
def chain(base_chain):
    chain = api.build(
        base_chain,
        api.mine_blocks(3),
    )
    assert chain.get_canonical_head().block_number == 3
    return chain


def test_import_block_with_reorg(chain, funded_address_private_key):
    # mine ahead 3 blocks on the fork chain
    fork_chain = api.build(
        chain,
        api.copy(),
        api.mine_block(extra_data=b'fork-it'),
        api.mine_blocks(2),
    )
    # mine ahead 2 blocks on the main chain
    main_chain = api.build(
        chain,
        api.mine_blocks(2)
    )

    block_4 = main_chain.get_canonical_block_by_number(4)
    f_block_4 = fork_chain.get_canonical_block_by_number(4)

    assert f_block_4.number == block_4.number == 4
    assert f_block_4 != block_4
    assert f_block_4.header.difficulty <= block_4.header.difficulty

    block_5 = main_chain.get_canonical_block_by_number(5)
    f_block_5 = fork_chain.get_canonical_block_by_number(5)

    assert f_block_5.number == block_5.number == 5
    assert f_block_5 != block_5
    assert f_block_5.header.difficulty <= block_5.header.difficulty

    f_block_6 = fork_chain.get_canonical_block_by_number(6)
    pre_reorg_chain_head = main_chain.header

    # now we proceed to import the blocks from the fork chain into the main
    # chain.  Blocks 4 and 5 should import resulting in no re-organization.
    for block in (f_block_4, f_block_5):
        _, new_canonical_blocks, old_canonical_blocks = main_chain.import_block(block)
        assert not new_canonical_blocks
        assert not old_canonical_blocks
        # ensure that the main chain head has not changed.
        assert main_chain.header == pre_reorg_chain_head

    # now we import block 6 from the fork chain.  This should cause a re-org.
    _, new_canonical_blocks, old_canonical_blocks = main_chain.import_block(f_block_6)
    assert new_canonical_blocks == (f_block_4, f_block_5, f_block_6)
    assert old_canonical_blocks == (block_4, block_5)

    assert main_chain.get_canonical_head() == f_block_6.header


def test_import_block_with_reorg_with_current_head_as_uncle(
        chain,
        funded_address_private_key):
    """
    https://github.com/ethereum/py-evm/issues/1185
    """

    main_chain, fork_chain = api.build(
        chain,
        api.chain_split(
            (api.mine_block(),),  # this will be the 'uncle'
            (api.mine_block(extra_data=b'fork-it'),),
        ),
    )

    # the chain head from the main chain will become an uncle once we
    # import the fork chain blocks.
    uncle = main_chain.get_canonical_head()

    fork_chain = api.build(
        fork_chain,
        api.mine_block(uncles=(uncle,)),
    )
    head = fork_chain.get_canonical_head()
    block_a = fork_chain.get_canonical_block_by_number(head.block_number - 1)
    block_b = fork_chain.get_canonical_block_by_number(head.block_number)

    final_chain = api.build(
        main_chain,
        api.import_block(block_a),
        api.import_block(block_b),  # the block with the uncle.
    )

    header = final_chain.get_canonical_head()
    block = final_chain.get_canonical_block_by_number(header.block_number)

    assert len(block.uncles) == 1
