# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------------
#
#   Copyright 2018-2019 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.
#
# ------------------------------------------------------------------------------

"""This module contains the tests of the wallet module."""

from unittest import TestCase

import eth_account
import pytest

from aea.crypto.cosmos import CosmosCrypto
from aea.crypto.ethereum import EthereumCrypto
from aea.crypto.fetchai import FetchAICrypto
from aea.crypto.wallet import Wallet
from aea.exceptions import AEAException

from tests.conftest import (
    COSMOS_PRIVATE_KEY_PATH,
    ETHEREUM_PRIVATE_KEY_PATH,
    FETCHAI_PRIVATE_KEY_PATH,
)


def test_wallet_initialisation_error():
    """Test the value error when we initialise the wallet."""
    with pytest.raises(AEAException):
        Wallet({"Test": "test"})


class WalletTestCase(TestCase):
    """Test case for Wallet class."""

    def test_wallet_init_positive(self):
        """Test Wallet init positive result."""
        private_key_paths = {
            EthereumCrypto.identifier: ETHEREUM_PRIVATE_KEY_PATH,
            FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_PATH,
            CosmosCrypto.identifier: COSMOS_PRIVATE_KEY_PATH,
        }
        Wallet(private_key_paths)

    def test_wallet_init_bad_id(self):
        """Test Wallet init unsupported private key paths identifier."""
        private_key_paths = {"unknown_id": "path1"}
        with self.assertRaises(AEAException):
            Wallet(private_key_paths)

    def test_wallet_init_bad_paths(self):
        """Test Wallet init with bad paths to private keys"""
        private_key_paths = {FetchAICrypto.identifier: "this_path_does_not_exists"}
        with self.assertRaises(FileNotFoundError):
            Wallet(private_key_paths)

    def test_wallet_crypto_objects_positive(self):
        """Test Wallet.crypto_objects init positive result."""
        private_key_paths = {
            EthereumCrypto.identifier: ETHEREUM_PRIVATE_KEY_PATH,
            FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_PATH,
        }
        wallet = Wallet(private_key_paths)
        crypto_objects = wallet.crypto_objects
        self.assertTupleEqual(
            tuple(crypto_objects), (EthereumCrypto.identifier, FetchAICrypto.identifier)
        )

    def test_wallet_public_keys_positive(self):
        """Test Wallet.public_keys init positive result."""
        private_key_paths = {
            EthereumCrypto.identifier: ETHEREUM_PRIVATE_KEY_PATH,
            FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_PATH,
        }
        wallet = Wallet(private_key_paths)
        public_keys = wallet.public_keys
        self.assertTupleEqual(
            tuple(public_keys), (EthereumCrypto.identifier, FetchAICrypto.identifier)
        )

    def test_wallet_addresses_positive(self):
        """Test Wallet.addresses init positive result."""
        private_key_paths = {
            EthereumCrypto.identifier: ETHEREUM_PRIVATE_KEY_PATH,
            FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_PATH,
        }
        wallet = Wallet(private_key_paths)
        addresses = wallet.addresses
        self.assertTupleEqual(
            tuple(addresses), (EthereumCrypto.identifier, FetchAICrypto.identifier)
        )

    def test_wallet_private_keys_positive(self):
        """Test Wallet.private_keys init positive result."""
        private_key_paths = {
            EthereumCrypto.identifier: ETHEREUM_PRIVATE_KEY_PATH,
            FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_PATH,
        }
        wallet = Wallet(private_key_paths)
        private_keys = wallet.private_keys
        self.assertTupleEqual(
            tuple(private_keys), (EthereumCrypto.identifier, FetchAICrypto.identifier)
        )

    def test_wallet_cryptos_positive(self):
        """Test Wallet.main_cryptos and connection cryptos init positive result."""
        private_key_paths = {
            EthereumCrypto.identifier: ETHEREUM_PRIVATE_KEY_PATH,
            FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_PATH,
        }
        connection_private_key_paths = {
            EthereumCrypto.identifier: ETHEREUM_PRIVATE_KEY_PATH,
            FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_PATH,
        }
        wallet = Wallet(private_key_paths, connection_private_key_paths)
        assert len(wallet.main_cryptos.crypto_objects) == len(
            wallet.connection_cryptos.crypto_objects
        ), "Incorrect amount of cryptos"

    def test_wallet_sign_message_positive(self):
        """Test Wallet.sign_message positive result."""
        private_key_paths = {
            EthereumCrypto.identifier: ETHEREUM_PRIVATE_KEY_PATH,
            FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_PATH,
        }
        wallet = Wallet(private_key_paths)
        signature = wallet.sign_message(
            EthereumCrypto.identifier, message=b"some message"
        )
        assert type(signature) == str and int(
            signature, 16
        ), "No signature present or not hexadecimal"

    def test_wallet_sign_message_negative(self):
        """Test Wallet.sign_message negative result."""
        private_key_paths = {
            EthereumCrypto.identifier: ETHEREUM_PRIVATE_KEY_PATH,
            FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_PATH,
        }
        wallet = Wallet(private_key_paths)
        signature = wallet.sign_message("unknown id", message=b"some message")
        assert signature is None, "Signature should be none"

    def test_wallet_sign_transaction_positive(self):
        """Test Wallet.sign_transaction positive result."""
        private_key_paths = {
            EthereumCrypto.identifier: ETHEREUM_PRIVATE_KEY_PATH,
            FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_PATH,
        }
        wallet = Wallet(private_key_paths)
        signed_transaction = wallet.sign_transaction(
            EthereumCrypto.identifier,
            transaction={"gasPrice": 50, "nonce": 10, "gas": 10},
        )
        assert (
            type(signed_transaction) == eth_account.datastructures.SignedTransaction
        ), "No signed transaction returned"

    def test_wallet_sign_transaction_negative(self):
        """Test Wallet.sign_transaction negative result."""
        private_key_paths = {
            EthereumCrypto.identifier: ETHEREUM_PRIVATE_KEY_PATH,
            FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_PATH,
        }
        wallet = Wallet(private_key_paths)
        signed_transaction = wallet.sign_transaction(
            "unknown id", transaction={"this is my tx": "here"}
        )
        assert signed_transaction is None, "Signed transaction should be none"
