# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------------
#
#   Copyright 2018-2021 Fetch.AI Limited
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#
# ------------------------------------------------------------------------------

""" gRPC example of atomic swap using ERC1155 contract and multiple messages per transaction """

import inspect
import os
from pathlib import Path
from typing import Any, Dict

from grpc import insecure_channel

from cosmpy.clients.signing_cosmwasm_client import SigningCosmWasmClient
from cosmpy.crypto.keypairs import PrivateKey

# ID and amount of tokens to be minted in contract
TOKEN_ID_1 = "680564733841876926926749214863536422912"
AMOUNT_1 = "1"
TOKEN_ID_2 = "680564733841876926926749214863536422913"
AMOUNT_2 = "1"

# Path to smart contract
CUR_PATH = os.path.dirname(inspect.getfile(inspect.currentframe()))  # type: ignore
CONTRACT_FILENAME = Path(os.path.join(CUR_PATH, "..", "contracts", "cw_erc1155.wasm"))

# Node config
GRPC_ENDPOINT_ADDRESS = "localhost:9090"
CHAIN_ID = "testing"

# Private key of validator's account
VALIDATOR_PK = PrivateKey(
    bytes.fromhex("0ba1db680226f19d4a2ea64a1c0ea40d1ffa3cb98532a9fa366994bb689a34ae")
)

# Private key of bob's account
BOB_PK = PrivateKey(
    bytes.fromhex("439861b21d146e83fe99496f4998a305c83cfbc24717c77e32b06d224bf1e636")
)

# Create clients
channel = insecure_channel(GRPC_ENDPOINT_ADDRESS)
validator_client = SigningCosmWasmClient(VALIDATOR_PK, channel, CHAIN_ID)
bob_client = SigningCosmWasmClient(BOB_PK, channel, CHAIN_ID)

# Store contract
code_id = validator_client.deploy_contract(CONTRACT_FILENAME)
print(f"Contract stored, code ID: {code_id}")

# Init contract
init_msg: Dict[str, Any] = {}
contract_address = validator_client.instantiate_contract(code_id, init_msg)
print(f"Contract address: {contract_address}")

# Create 2 tokens in one batch message
create_batch_msg = {
    "create_batch": {
        "item_owner": str(validator_client.address),
        "tokens": [
            {
                "id": TOKEN_ID_1,
                "path": "some_path",
            },
            {
                "id": TOKEN_ID_2,
                "path": "some_path",
            },
        ],
    }
}
response = validator_client.execute_contract(contract_address, create_batch_msg)
print(f"Created tokens with ID {TOKEN_ID_1} and {TOKEN_ID_2}")

# Mint 1 token with ID TOKEN_ID_1 and give it to validator
mint_single_msg = {
    "mint_single": {
        "to_address": str(validator_client.address),
        "id": TOKEN_ID_1,
        "supply": AMOUNT_1,
        "data": "some_data",
    },
}
response = validator_client.execute_contract(contract_address, mint_single_msg)
print(f"Minted 1 token with ID {TOKEN_ID_1} to validator.")

# Mint 1 token with ID TOKEN_ID_2 and give it to bob
mint_single_msg = {
    "mint_single": {
        "to_address": str(bob_client.address),
        "id": TOKEN_ID_2,
        "supply": AMOUNT_2,
        "data": "some_data",
    },
}
response = validator_client.execute_contract(contract_address, mint_single_msg)
print(f"Minted 1 token with ID {TOKEN_ID_2} to bob")

# Create atomic swap messages
# Message to transfer 1 TOKEN_1 from validator to bob
transfer_msg_1 = {
    "transfer_single": {
        "operator": str(validator_client.address),
        "from_address": str(validator_client.address),
        "to_address": str(bob_client.address),
        "id": TOKEN_ID_1,
        "value": AMOUNT_1,
    },
}
exec_transfer_msg_1 = validator_client.get_packed_exec_msg(
    sender_address=validator_client.address,
    contract_address=contract_address,
    msg=transfer_msg_1,
)

# Message to transfer 1 TOKEN_2 from bob to validator
transfer_msg_2 = {
    "transfer_single": {
        "operator": str(bob_client.address),
        "from_address": str(bob_client.address),
        "to_address": str(validator_client.address),
        "id": TOKEN_ID_2,
        "value": AMOUNT_2,
    },
}
exec_transfer_msg_2 = validator_client.get_packed_exec_msg(
    sender_address=bob_client.address,
    contract_address=contract_address,
    msg=transfer_msg_2,
)

# Execute atomic swap by 2 transfer messages in one transaction
tx = validator_client.generate_tx(
    [exec_transfer_msg_1, exec_transfer_msg_2],
    [validator_client.address, bob_client.address],
    [validator_client.public_key_bytes, bob_client.public_key_bytes],
    gas_limit=2000000,
)
validator_client.sign_tx(tx)
bob_client.sign_tx(tx)
response = validator_client.broadcast_tx(tx)
print(f"Swapped token validator's token {TOKEN_ID_1} with bob's token {TOKEN_ID_2}")

# Query validator's balances of validator an bob in one query call
msg = {
    "balance_batch": {
        "addresses": [
            {
                "address": str(bob_client.address),
                "id": TOKEN_ID_1,
            },
            {
                "address": str(validator_client.address),
                "id": TOKEN_ID_2,
            },
        ]
    }
}
res = validator_client.query_contract_state(contract_address=contract_address, msg=msg)

# Check if swap was successful
# TOKEN_ID_1 should be now owned by bob and TOKEN_ID_2 by validator
assert res["balances"] == [AMOUNT_1, AMOUNT_2]

print("All done!")
