import logging

logger = logging.getLogger(__name__)


class Wallet:
    """
    The wallet is responsible for signing transactions and messages by using an account's
    private key.

    The private key is always red from the encrypted keyfile and is never saved in memory beyond
    the life span of the signing function.

    The use of this wallet allows Nevermined tools to send rawTransactions which keeps the user
    key and password safe and they are never sent outside. Another advantage of this is that
    we can interact directly with remote keeper nodes without having to run a local parity
    node since we only send the raw transaction hash so the user info is safe.

    Note that currently a local node is still necessary for completing the consume flow because
    the secretstore needs a parity node to encrypt/decrypt the asset urls. We could eliminate
    this dependency on local parity node by using Gateway's publish endpoint for encryption and
    sending url index to Gateway's consume endpoint instead of decrypting locally first.
    The secretstore also uses the parity node to sign the document key id which is necessary
    for communicating with the secret store nodes.

    """
    _last_tx_count = dict()
    MIN_GAS_PRICE = 1000000000

    def __init__(self, web3, key_file, password, address=None):
        self._web3 = web3
        self._key_file = key_file
        self._password = password
        self._address = address

    def __read_key(self):
        with open(self._key_file) as _file:
            encrypted_key = _file.read()
            private_key = self._web3.eth.account.decrypt(encrypted_key, self._password)
            return private_key

    def validate(self):
        key = self.__read_key()
        account = self._web3.eth.account.from_key(key)
        return account.address == self._address

    @staticmethod
    def _get_nonce(web3, address):
        # We cannot rely on `web3.eth.getTransactionCount` because when sending multiple
        # transactions in a row without wait in between the network may not get the chance to
        # update the transaction count for the account address in time.
        # So we have to manage this internally per account address.
        if address not in Wallet._last_tx_count:
            Wallet._last_tx_count[address] = web3.eth.getTransactionCount(address)
        else:
            Wallet._last_tx_count[address] += 1

        return Wallet._last_tx_count[address]

    def sign_tx(self, tx):
        private_key = self.__read_key()
        account = self._web3.eth.account.from_key(private_key)
        # nonce = Wallet._get_nonce(self._web3, account.address)
        # logger.debug(f'`Wallet` signing tx: sender address: {account.address} nonce: {nonce}, '
        #              f'gasprice: {self._web3.eth.gasPrice}')
        signed_tx = 0
        try:
            priority_fee = self._web3.eth.max_priority_fee
            gas_price = self._web3.eth.gas_price
            tx['type'] = 2
            tx['nonce'] = self._web3.eth.getTransactionCount(account.address)
            tx['maxPriorityFeePerGas'] = priority_fee
            tx['maxFeePerGas'] = gas_price
            signed_tx = self._web3.eth.account.sign_transaction(tx, private_key)
        except:
            gas_price = self._web3.eth.gas_price
            tx['nonce'] = self._web3.eth.getTransactionCount(account.address)
            tx['gasPrice'] = gas_price
            signed_tx = self._web3.eth.account.sign_transaction(tx, private_key)
        logger.debug(f'`Wallet` signed tx is {signed_tx}')
        return signed_tx.rawTransaction

    def sign(self, msg_hash):
        private_key = self.__read_key()
        account = self._web3.eth.account.from_key(private_key)
        return account.signHash(msg_hash)
