import base64
import hashlib
import os
import socket
import uuid
from typing import List, Optional

from clarabot_crypto.group_config.chain import BlockName

from clarabot_nano.common.config.constants import LocalSecrets
from clarabot_nano.utility.serialize import uint2bytes, ulong2bytes


def get_host_identifier() -> bytes:
    """
    This is used only locally (and even then it is hashed doubly in storage to be beyond recognition) to detect if
    the configs were copied over from another machine to automatically generate new secrets for security.
    """
    node = uuid.getnode()
    if node & (1 << 40):  # random node is not usable, fall back to hostname
        node = socket.gethostname().encode()
    else:
        node = ulong2bytes(node)
    return hashlib.sha3_256(node).digest()


def get_secrets_lengths():
    return [32 if i < LocalSecrets.LENGTH_32_BEFORE else 64 for i in range(LocalSecrets.FINGERPRINT)]


def get_secrets_fingerprint(secrets: List[bytes]):
    raw = [get_host_identifier(), b"".join(bytes([length]) for length in get_secrets_lengths())]
    for i in range(len(secrets) - 1):
        raw.append(uint2bytes(len(secrets[i])))
        raw.append(secrets[i])
    return hashlib.sha3_256(b"".join(raw)).digest()


def create_new_secrets():
    secrets = []
    for length in get_secrets_lengths():
        secrets.append(os.urandom(length))
    secrets.append(b"")
    secrets[-1] = get_secrets_fingerprint(secrets)
    return secrets


def block_name_to_config_format(block_name: BlockName) -> str:
    return block_name


def block_name_from_config_format(block_name: BlockName) -> Optional[BlockName]:
    """
    Errors are ignored so config blockchain verification will fall back to unverified and the Nano will still work.
    """
    if block_name.find("|") > 0:
        return block_name
    # convert older "id_hash" to new "name" format
    try:
        id_, _, hash_ = block_name.partition(":")
        return f"{id_}|{base64.urlsafe_b64encode(base64.b64decode(hash_)).decode('ascii')}"
    except Exception:
        pass
