import web3
from web3 import Web3
from web3.auto import w3
from apexpro import constants

PREPEND_DEC = '\x19Ethereum Signed Message:\n32'
PREPEND_HEX = '\x19Ethereum Signed Message:\n\x20'


def is_valid_sig_type(
    sig_type,
):
    return sig_type in [
        constants.SIGNATURE_TYPE_DECIMAL,
        constants.SIGNATURE_TYPE_HEXADECIMAL,
        constants.SIGNATURE_TYPE_NO_PREPEND,
        constants.SIGNATURE_TYPE_PERSONAL,
    ]


def ec_recover_typed_signature(
    hashVal,
    typed_signature,
):
    if len(strip_hex_prefix(typed_signature)) != 66 * 2:
        raise Exception('Unable to ecrecover signature: ' + typed_signature)

    sig_type = int(typed_signature[-2:], 16)
    prepended_hash = ''
    if sig_type == constants.SIGNATURE_TYPE_NO_PREPEND:
        prepended_hash = hashVal
    elif sig_type == constants.SIGNATURE_TYPE_DECIMAL:
        prepended_hash = Web3.solidityKeccak(
            ['string', 'bytes32'],
            [PREPEND_DEC, hashVal],
        )
    elif sig_type == constants.SIGNATURE_TYPE_HEXADECIMAL:
        prepended_hash = Web3.solidityKeccak(
            ['string', 'bytes32'],
            [PREPEND_HEX, hashVal],
        )
    else:
        raise Exception('Invalid signature type: ' + sig_type)

    if not prepended_hash:
        raise Exception('Invalid hash: ' + hashVal)

    signature = typed_signature[:-2]

    address = w3.eth.account.recoverHash(prepended_hash, signature=signature)
    return address


def create_typed_signature(
    signature,
    sig_type,
):
    if not is_valid_sig_type(sig_type):
        raise Exception('Invalid signature type: ' + sig_type)

    return fix_raw_signature(signature) + '0' + str(sig_type)


def fix_raw_signature(
    signature,
):
    stripped = strip_hex_prefix(signature)

    if len(stripped) != 130:
        raise Exception('Invalid raw signature: ' + signature)

    rs = stripped[:128]
    v = stripped[128: 130]

    if v == '00':
        return '0x' + rs + '1b'
    if v == '01':
        return '0x' + rs + '1c'
    if v in ['1b', '1c']:
        return '0x' + stripped

    raise Exception('Invalid v value: ' + v)

# ============ Byte Helpers ============


def strip_hex_prefix(input):
    if input.startswith('0x'):
        return input[2:]

    return input


def addresses_are_equal(
    address_one,
    address_two,
):
    if not address_one or not address_two:
        return False

    return (
        strip_hex_prefix(
            address_one
        ).lower() == strip_hex_prefix(address_two).lower()
    )


def hash_string(input):
    return Web3.solidityKeccak(['string'], [input])

def hash_person(input):
    msg = "\x19Ethereum Signed Message:\n%d%s" %( len(input), input)
    return Web3.solidityKeccak(['string'], [msg])
