import string
import struct
from base64 import b32encode, b32decode

from pyblake2 import blake2b

from . import ed25519_blake2

maketrans = hasattr(bytes, 'maketrans') and bytes.maketrans or string.maketrans
B32_ALPHABET = b'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'
XRB_ALPHABET = b'13456789abcdefghijkmnopqrstuwxyz'
XRB_ENCODE_TRANS = maketrans(B32_ALPHABET, XRB_ALPHABET)
XRB_DECODE_TRANS = maketrans(XRB_ALPHABET, B32_ALPHABET)


def address_checksum(address):
    """
    Returns the checksum in bytes for an address in bytes
    """
    address_bytes = address
    h = blake2b(digest_size=5)
    h.update(address_bytes)
    checksum = bytearray(h.digest())
    checksum.reverse()
    return checksum


def private_to_public_key(private_key):
    """
    Returns the public key for a private key

    :param private_key: private key (in bytes) to get public key for
    :type private_key: bytes
    """
    return ed25519_blake2.publickey_unsafe(private_key)


def keypair_from_seed(seed, index=0):
    """
    Generates a deterministic keypair from `seed` based on `index`

    :param seed: bytes value of seed
    :type seed: bytes

    :param index: offset from seed
    :type index: int

    :return: dict of the form: {
        'private': private_key
        'public': public_key
    }
    """

    h = blake2b(digest_size=32)
    h.update(seed + struct.pack(">L", index))
    priv_key = h.digest()
    pub_key = private_to_public_key(priv_key)
    return {'private': priv_key, 'public': pub_key}


def b32xrb_encode(value):
    """
    Encodes bytes to xrb encoding which uses the base32 algorithm
    with a custom alphabet: '13456789abcdefghijkmnopqrstuwxyz'

    :param value: the value to encode
    :type: bytes

    :return: encoded value
    :rtype: bytes

    >>> b32xrb_encode(b'deadbeef')
    b'ejkp4s54eokpe==='
    """
    return b32encode(value).translate(XRB_ENCODE_TRANS)


def b32xrb_decode(value):
    """
    Decodes a value in xrb encoding to bytes using base32 algorithm
    with a custom alphabet: '13456789abcdefghijkmnopqrstuwxyz'

    :param value: the value to decode
    :type: bytes

    :return: decoded value
    :rtype: bytes

    >>> b32xrb_decode(b'fxop4ya=')
    b'okay'
    """
    return b32decode(value.translate(XRB_DECODE_TRANS))


def verify_signature(message, signature, public_key):
    """
    Verifies `signature` is correct for a `message` signed with `public_key`

    :param message: message to check
    :type message: bytes

    :param signature: signature to check
    :type signature: bytes

    :param public_key: public_key to check
    :type public_key: bytes

    :return: True if valid, False otherwise
    :rtype: bool
    """

    try:
        ed25519_blake2.checkvalid(signature, message, public_key)
    except ed25519_blake2.SignatureMismatch:
        return False
    return True


def sign_message(message, private_key, public_key=None):
    """
    Signs a `message` using `private_key` and `public_key`

    .. warning:: Not safe to use with secret keys or secret data. See module
                 docstring.  This function should be used for testing only.

    :param message: the message to sign
    :type message: bytes

    :param private_key: private key used to sign message
    :type private_key: bytes

    :param public_key: public key used to sign message
    :type public_key: bytes

    :return: the signature of the signed message
    :rtype: bytes
    """

    if public_key is None:
        public_key = private_to_public_key(private_key)

    return ed25519_blake2.signature_unsafe(message, private_key, public_key)