# Copyright © 2020 Interplanetary Database Association e.V.,
# Planetmint and IPDB software contributors.
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
# Code is Apache-2.0 and docs are CC-BY-4.0

# # Basic Acceptance Test
# Here we check that the primitives of the system behave as expected.
# As you will see, this script tests basic stuff like:
#
# - create a transaction
# - check if the transaction is stored
# - check for the outputs of a given public key
# - transfer the transaction to another key
#
# We run a series of checks for each steps, that is retrieving the transaction from
# the remote system, and also checking the `outputs` of a given public key.
#
# This acceptance test is a rip-off of our
# [tutorial](https://docs.planetmint.com/projects/py-driver/en/latest/usage.html).

# ## Imports
# We need some utils from the `os` package, we will interact with
# env variables.
import os

# For this test case we import and use the Python Driver.
from planetmint_driver import Planetmint
from planetmint_driver.crypto import generate_keypair


def test_basic():
    # ## Set up a connection to Planetmint
    # To use BighainDB we need a connection. Here we create one. By default we
    # connect to localhost, but you can override this value using the env variable
    # called `PLANETMINT_ENDPOINT`, a valid value must include the schema:
    # `https://example.com:9984`
    bdb = Planetmint(os.environ.get('PLANETMINT_ENDPOINT'))

    # ## Create keypairs
    # This test requires the interaction between two actors with their own keypair.
    # The two keypairs will be called—drum roll—Alice and Bob.
    alice, bob = generate_keypair(), generate_keypair()

    # ## Alice registers her bike in Planetmint
    # Alice has a nice bike, and here she creates the "digital twin"
    # of her bike.
    bike = {'data': {'bicycle': {'serial_number': 420420}}}

    # She prepares a `CREATE` transaction...
    prepared_creation_tx = bdb.transactions.prepare(
            operation='CREATE',
            signers=alice.public_key,
            asset=bike)

    # ... and she fulfills it with her private key.
    fulfilled_creation_tx = bdb.transactions.fulfill(
            prepared_creation_tx,
            private_keys=alice.private_key)

    # We will use the `id` of this transaction several time, so we store it in
    # a variable with a short and easy name
    bike_id = fulfilled_creation_tx['id']

    # Now she is ready to send it to the Planetmint Network.
    sent_transfer_tx = bdb.transactions.send_commit(fulfilled_creation_tx)

    # And just to be 100% sure, she also checks if she can retrieve
    # it from the Planetmint node.
    assert bdb.transactions.retrieve(bike_id), 'Cannot find transaction {}'.format(bike_id)

    # Alice is now the proud owner of one unspent asset.
    assert len(bdb.outputs.get(alice.public_key, spent=False)) == 1
    assert bdb.outputs.get(alice.public_key)[0]['transaction_id'] == bike_id

    # ## Alice transfers her bike to Bob
    # After registering her bike, Alice is ready to transfer it to Bob.
    # She needs to create a new `TRANSFER` transaction.

    # A `TRANSFER` transaction contains a pointer to the original asset. The original asset
    # is identified by the `id` of the `CREATE` transaction that defined it.
    transfer_asset = {'id': bike_id}

    # Alice wants to spend the one and only output available, the one with index `0`.
    output_index = 0
    output = fulfilled_creation_tx['outputs'][output_index]

    # Here, she defines the `input` of the `TRANSFER` transaction. The `input` contains
    # several keys:
    #
    # - `fulfillment`, taken from the previous `CREATE` transaction.
    # - `fulfills`, that specifies which condition she is fulfilling.
    # - `owners_before`.
    transfer_input = {'fulfillment': output['condition']['details'],
                      'fulfills': {'output_index': output_index,
                                   'transaction_id': fulfilled_creation_tx['id']},
                      'owners_before': output['public_keys']}

    # Now that all the elements are set, she creates the actual transaction...
    prepared_transfer_tx = bdb.transactions.prepare(
            operation='TRANSFER',
            asset=transfer_asset,
            inputs=transfer_input,
            recipients=bob.public_key)

    # ... and signs it with her private key.
    fulfilled_transfer_tx = bdb.transactions.fulfill(
            prepared_transfer_tx,
            private_keys=alice.private_key)

    # She finally sends the transaction to a Planetmint node.
    sent_transfer_tx = bdb.transactions.send_commit(fulfilled_transfer_tx)

    # And just to be 100% sure, she also checks if she can retrieve
    # it from the Planetmint node.
    assert bdb.transactions.retrieve(fulfilled_transfer_tx['id']) == sent_transfer_tx

    # Now Alice has zero unspent transactions.
    assert len(bdb.outputs.get(alice.public_key, spent=False)) == 0

    # While Bob has one.
    assert len(bdb.outputs.get(bob.public_key, spent=False)) == 1

    # Bob double checks what he got was the actual bike.
    bob_tx_id = bdb.outputs.get(bob.public_key, spent=False)[0]['transaction_id']
    assert bdb.transactions.retrieve(bob_tx_id) == sent_transfer_tx
