import copy
import hashlib
import os
import requests

from .syfr import *

DATA_BLOCK_SIZE = 65536


def encrypt_file(file_path, rsa_priv, receiver_pubkey):
    contents = open(file_path).read()

    return blocks


def masters_from_children(children, rsa_priv, receiver_pubkey):
    contents = []
    masters_content = []
    last_master_content = "MASTER:"

    for child in children:
        if len(last_master_content) + len(child['id']) > DATA_BLOCK_SIZE - bitsize_marker_length - 32:
            masters_content.append(last_master_content)
            last_master_content = "MASTER:"

        last_master_content += "\n{0}".format(child['id'])

    if len(last_master_content) > 8:
        masters_content.append(last_master_content)

    masters = [encrypt_block(long_pad(master_c), rsa_priv, receiver_pubkey) for master_c in masters_content]
    return masters


def fetch_block(id):
    url = "https://syfr.io/data/v0/{0}".format(id)
    print("Fetching block {0} from {1}.".format(id, url))
    return requests.get(url).content


def tree_decrypt_block(block, priv):
    """
    In parsing a tree, determine if the block is a master block.
    If so, return TRUE and a list of ids.
    Else it is a leaf-content block, return FALSE and whole contents.
    Whole block dict assumed to be passed in.  If ID passed in, try to fetch.
    """

    if isinstance(block, str):
        block = fetch_block(id)

    contents = long_unpad(full_decrypt_block(block, priv))
    if contents[0:7] == "MASTER:":
        return True, contents[7:].split('\n')[1:]
    else:
        return False, contents


def tree_decrypt(block, priv, cached_blocks=None):
    """
    Decrypts and assembles an entire block tree.
    If blocks are provided, checks here for cached blocks, otherwise it fetches
    on Internet.
    """
    content = ""
    cont = True
    blocks = [block]

    if isinstance(cached_blocks, list):
        cb = dict([(b['id'], b) for b in cached_blocks])
        cached_blocks = cb
    level = 0

    while cont:
        new_contents = []
        print("Decrypting {0} blocks at level {1}.".format(len(blocks), level))
        level += 1
        for b in blocks:
            if cached_blocks and isinstance(b, str) and b in cached_blocks:
                new_contents.append(tree_decrypt_block(cached_blocks[b], priv))
            else:
                new_contents.append(tree_decrypt_block(b, priv))

        blocks = []
        for is_master, con in new_contents:
            if not is_master:
                content += con
            else:
                blocks += con
        cont = len(blocks) > 0

    return content


def assemble_block_tree(contents, rsa_priv, receiver_pubkey):
    content_pieces = divide_contents(contents)
    print("Encrypting {0} Leaf Blocks.".format(len(content_pieces)))
    leaf_blocks = [encrypt_block(c, rsa_priv, receiver_pubkey) for c in content_pieces]
    blocks = copy.copy(leaf_blocks)
    n = len(blocks)
    new_blocks = blocks

    while n > 1:
        new_blocks = masters_from_children(new_blocks, rsa_priv, receiver_pubkey)
        print("APPENDING {0} Masters".format(len(new_blocks)))
        blocks += new_blocks
        n = len(new_blocks)

    return blocks


def divide_contents(contents):
    subcontents = []
    n = 0
    while n < len(contents):
        m = min(len(contents), n + DATA_BLOCK_SIZE - bitsize_marker_length)
        subcontent = contents[n:m]
        subcontent = long_pad(subcontent, DATA_BLOCK_SIZE)
        subcontents.append(subcontent)
        n += DATA_BLOCK_SIZE - bitsize_marker_length
    return subcontents


def unite_contents(content_blocks):
    content = ""
    for n, x in enumerate(content_blocks):
        content += long_unpad(x)

    return content


def compute_block_hash(block_dict):
    s = ""
    for k in sorted(block_dict.keys()):
        if k in ['id']:
            continue
        s += "&{0}:{1}".format(k, block_dict[k])
    return hashlib.sha256(s).hexdigest()


def decompose_metadata(metadata):
    sender, receiver = [x.split(':')[-1] for x in metadata.split(';')]
    return sender, receiver


def recompose_metadata(sender, receiver):
    # TODO remove this
    return "sender_pubkey:{0};receiver_pubkey:{1}".format(sender, receiver)


def encrypt_block(content, rsa_priv, receiver_pubkey):
    assert len(content) == DATA_BLOCK_SIZE
    aes_ciphertext, encry_aes_key, hmac, hmac_signature, iv, metadata = \
        encrypt(content, rsa_priv, receiver_pubkey)
    sender, receiver = decompose_metadata(metadata)
    response = {
                'aes_ciphertext': aes_ciphertext,
                'encry_aes_key': encry_aes_key,
                'hmac': hmac,
                'hmac_signature': hmac_signature,
                'iv': iv,
                'sender_public_key': sender,
                'receiver_public_key': receiver
                }
    response['id'] = compute_block_hash(response)
    return response


def full_decrypt_block(response, receiver_privkey):
    assert compute_block_hash(response) == response['id']


    return decrypt(
            response['aes_ciphertext'],
            response['encry_aes_key'],
            response['hmac'],
            response['hmac_signature'],
            receiver_privkey,
            response['iv'],
            recompose_metadata(
                response['sender_public_key'], response['receiver_public_key'])
        )


def aes_decrypt_block(response, aes_key):
    return

# def aes_rsa_encrypt(message, recipient_rsa_pub):
#     aes_key = create_aes_key()
#     aes_ciphertext, iv = aes_encrypt(message, aes_key)
#     # hmac_key = hashlib.sha256(aes_key).hexdigest()

#     #sender_pubkey = serialize_pubkey(rsa_priv.public_key())
#     # recipient_rsa_pub = load_pubkey(receiver_pubkey)
#     #recipient_rsa_pub = receiver_pubkey
#     # metadata = loader.recompose_metadata(sender_pubkey, receiver_pubkey)

#     encry_aes_key = rsa_encrypt(aes_key, recipient_rsa_pub)

#     # hmac_list = [metadata, iv, aes_ciphertext, encry_aes_key]
#     # hmac = create_hmac(hmac_key, hmac_list)
#     # hmac_signature = sign(hmac, rsa_priv)

#     # return aes_ciphertext, encry_aes_key, hmac, hmac_signature, iv, metadata
#     return aes_ciphertext, encry_aes_key, iv #, sign


# def aes_rsa_decrypt(aes_ciphertext, encry_aes_key, iv, rsa_priv): #, hmac, hmac_signature, rsa_priv, iv, metadata):
#     aes_key = rsa_decrypt(encry_aes_key, rsa_priv)

#     # hmac_key = hashlib.sha256(aes_key).hexdigest()
#     # hmac_list = [metadata, iv, aes_ciphertext, encry_aes_key]
#     # independent_hmac = create_hmac(hmac_key, hmac_list)
#     # assert hmac == independent_hmac

#     # sender_pub, receiver_pub = [x.split(':')[-1] for x in metadata.split(';')]
#     # sender_pub = load_pubkey(sender_pub)
#     #assert verify_signature(hmac_signature, hmac, sender_pub)

#     plaintext = aes_decrypt(aes_ciphertext, aes_key, iv)
#     return plaintext
