import web3
import importlib
from eth_account.messages import defunct_hash_message

from snet.sdk.utils.utils import RESOURCES_PATH, add_to_path

class PaymentChannel:
    def __init__(self, channel_id, w3, account, payment_channel_state_service_client, mpe_contract):
        self.channel_id = channel_id
        self.web3 = w3
        self.account = account
        self.mpe_contract = mpe_contract
        self.payment_channel_state_service_client = payment_channel_state_service_client
        self.state = {
            "nonce": 0,
            "last_signed_amount": 0
        }

    def add_funds(self, amount):
        return self.mpe_contract.channel_add_funds(self.account, self.channel_id, amount)

    def extend_expiration(self, expiration):
        return self.mpe_contract.channel_extend(self.account, self.channel_id, expiration)

    def extend_and_add_funds(self, expiration, amount):
        return self.mpe_contract.channel_extend_and_add_funds(self.account, self.channel_id, expiration, amount)

    def sync_state(self):
        channel_blockchain_data = self.mpe_contract.contract.functions.channels(self.channel_id).call()
        (current_nonce, last_signed_amount) = self._get_current_channel_state()
        nonce = channel_blockchain_data[0]
        total_amount = channel_blockchain_data[5]
        expiration = channel_blockchain_data[6]
        available_amount = total_amount - last_signed_amount
        self.state = {
            "current_nonce": current_nonce,
            "last_signed_amount": last_signed_amount,
            "nonce": nonce,
            "total_amount": total_amount,
            "expiration": expiration,
            "available_amount": available_amount
        }

    def _get_current_channel_state(self):
        stub = self.payment_channel_state_service_client
        current_block_number = self.web3.eth.get_block("latest").number
        message = web3.Web3.solidity_keccak(
            ["string", "address", "uint256", "uint256"],
            [
                "__get_channel_state",
                web3.Web3.to_checksum_address(self.mpe_contract.contract.address),
                self.channel_id,current_block_number
            ]
        )
        signature = self.web3.eth.account._sign_hash(defunct_hash_message(message), self.account.signer_private_key).signature
        with add_to_path(str(RESOURCES_PATH.joinpath("proto"))):
            state_service_pb2 = importlib.import_module("state_service_pb2")
        request = state_service_pb2.ChannelStateRequest(
            channel_id=web3.Web3.to_bytes(self.channel_id),
            signature=bytes(signature),
            current_block=current_block_number
        )
        response = stub.GetChannelState(request)
        return int.from_bytes(response.current_nonce, byteorder="big"),int.from_bytes(response.current_signed_amount, byteorder="big")
