"""
Library of common values and functions used by all other libraries in AutoNode.
"""

import getpass
import json
import logging
import os
import pickle

from pyhmy import (
    Typgpy,
    validator,
    exceptions,
)

user = getpass.getuser()
harmony_dir = f"{os.environ['HOME']}/.hmy"
node_dir = f"{os.environ['HOME']}/harmony_node"
node_sh_log_dir = f"{node_dir}/node_sh_logs"
bls_key_dir = f"{harmony_dir}/blskeys"
imported_wallet_pass_file_dir = f"{os.environ['HOME']}/harmony_wallet_pass"
cli_bin_dir = f"{harmony_dir}/bin"
cli_bin_path = f"{cli_bin_dir}/hmy"
saved_validator_path = f"{os.environ['HOME']}/harmony_validator_config.json"
saved_node_config_path = f"{harmony_dir}/.node_config.p"
tui_path = f"{harmony_dir}/tui"

node_script_source = "https://harmony.one/node.sh"
tui_source = "http://pub.harmony.one.s3.amazonaws.com/release/linux-x86_64/mainnet/static/harmony-tui"

default_cli_passphrase = ""
bls_key_len = 96
b32_addr_len = 42
check_interval = 8  # Estimated block time

msg_tag = "[AutoNode]"

# TODO: Keybase integration for identity
_validator_config_default = {
    "validator-addr": None,
    "name": None,
    "website": None,
    "security-contact": None,
    "identity": None,
    "amount": None,
    "min-self-delegation": None,
    "rate": None,
    "max-rate": None,
    "max-change-rate": None,
    "max-total-delegation": None,
    "details": None,
    "gas-price": "1"
}
validator_config = _validator_config_default.copy()

# Invariant: 'public-bls-keys' does not change after node is started.
_node_config_default = {
    "endpoint": "https://api.s0.b.hmny.io/",
    "network": "testnet",
    "clean": False,
    "shard": None,
    "auto-reset": False,
    "auto-active": False,
    "no-validator": False,
    "archival": False,
    "no-download": False,
    "fast-sync": False,
    "expose-rpc": False,
    "public-bls-keys": [],
    "encrypted-wallet-passphrase": b'',
    "_is_recovering": False  # Only used for auto hard-reset
}
node_config = _node_config_default.copy()


def save_validator_config():
    """
    Do not save invalid validator.
    In worst case, new validator information will be re-prompted on re-init of AutoNode.
    """
    if node_config['no-validator']:
        return
    try:
        for key, value in _validator_config_default.items():
            if key not in validator_config.keys():
                raise KeyError(f"Missing key {key} from validator config.")
        if validator_config['validator-addr'] is not None:  # Only validate if validator addr has been saved
            validator.Validator(validator_config['validator-addr']).load(validator_config)
        config_string = json.dumps(validator_config, indent=4)
    except (exceptions.InvalidValidatorError, json.decoder.JSONDecodeError, KeyError, TypeError) as e:
        log(f"{Typgpy.FAIL}Invalid validator information to save.{Typgpy.ENDC}\n"
            f"Error: {e}.\n"
            f"Validator Config: {json.dumps(validator_config, indent=2)}")
        log(f"{Typgpy.WARNING}NOT saving validator config, continuing...{Typgpy.ENDC}")
        return
    save_protected_file(config_string, saved_validator_path, verbose=False)
    # Make validator config file easily writeable
    os.chmod(saved_validator_path, 0o600)


def load_validator_config():
    """
    Load the saved validator config.

    Raises json.decoder.JSONDecodeError, IOError, PermissionError
    """
    with open(saved_validator_path, 'r', encoding='utf8') as f:
        imported_val_config = json.load(f)
        validator_config.update(imported_val_config)


def save_node_config():
    """
    Do not save invalid node config.
    In worst case, node config will be refreshed with valid info on re-init of AutoNode.
    """
    try:
        for key, value in _node_config_default.items():
            if key not in node_config.keys():
                raise KeyError(f"Missing key {key} from node config.")
        node_config_string = pickle.dumps(node_config)
    except (pickle.PickleError, KeyError, TypeError) as e:
        log(f"{Typgpy.FAIL}Invalid node config to save.{Typgpy.ENDC}\n"
            f"Error: {e}.")
        log(f"{Typgpy.WARNING}NOT saving node config, continuing...{Typgpy.ENDC}")
        return
    save_protected_file(node_config_string, saved_node_config_path, verbose=False)


def load_node_config():
    """
    Load the saved node config.

    Raises pickle.PickleError, IOError, PermissionError
    """
    with open(saved_node_config_path, 'rb') as f:
        imported_node_config = pickle.load(f)
        node_config.update(imported_node_config)


def save_protected_file(content, file_path, verbose=True):
    if os.path.isfile(file_path):
        if os.access(file_path, os.R_OK):  # check for min needed perms.
            os.remove(file_path)
        else:
            raise PermissionError(f"Cannot save protected file to {file_path} for user {user}")
    if isinstance(content, bytes):
        with open(file_path, 'wb') as f:
            f.write(content)
            protect_file(file_path, verbose=verbose)
    else:
        with open(file_path, 'w', encoding='utf8') as f:
            f.write(content)
            protect_file(file_path, verbose=verbose)


def reset_validator_config():
    validator_config.clear()
    validator_config.update(_validator_config_default)


def reset_node_config():
    node_config.clear()
    node_config.update(_node_config_default)


def log(*args):
    """
    Tagged print and log for AutoNode library.
    # TODO: implement and correct msgs for log levels
    """
    logging.getLogger('AutoNode').debug(*args)


def protect_file(file_path, verbose=True):
    """
    Protect a file with chmod 400.
    """
    if verbose:
        log(f"{Typgpy.WARNING}Protecting file `{file_path}` for user {user}{Typgpy.ENDC}")
    os.chmod(file_path, 0o400)
