#!/usr/bin/env python3
from libra_client.cli.color import support_color, print_color, bcolors
from libra_client.shell.client_proxy import ClientProxy
from libra_client.shell.dev_commands import DevCommand
from libra_client.shell.transfer_commands import TransferCommand
from libra_client.shell.query_commands import QueryCommand
from libra_client.shell.account_commands import AccountCommand
from libra_client.cli.ledger_cmds import LedgerCmd
from libra_client.cli.command import get_commands_alias, report_error, print_commands, parse_cmd
from libra_client import Client
from libra.version import version
from datetime import datetime
import argparse
import sys
import os
import signal
from libra_client.client import NETWORKS

TESTNET = NETWORKS['testnet']['url']


if os.name == 'posix':
    import readline


def get_commands(include_dev: bool):
    commands = [AccountCommand(), QueryCommand(), TransferCommand(), LedgerCmd()]
    if include_dev:
        commands.append(DevCommand())
    return get_commands_alias(commands)


def run_shell(args):  # noqa: C901
    grpc_client = Client.new(args.url, args.faucet_account_file)
    try:
        info = grpc_client.get_latest_ledger_info()
        time = datetime.fromtimestamp(info.timestamp / 1000_000)
        ledger_info_str = f"latest version = {info.version}, timestamp = {time}"
    except Exception as err:
        report_error(f"Not able to connect to validator at {args.url}", err, args.verbose)
        return
    client_proxy = ClientProxy(grpc_client, args)
    client_info = f"Connected to validator at: {grpc_client.url}, {ledger_info_str}"
    print(client_info)
    (commands, alias_to_cmd) = get_commands(grpc_client.faucet_account is not None)
    while True:
        prompt = "libra% "
        if support_color():
            prompt = f'\033[91m{prompt}\033[0m'
        try:
            line = input(prompt)
        except EOFError:
            sys.exit(0)
        params = parse_cmd(line)
        if len(params) == 0:
            continue
        cmd = alias_to_cmd.get(params[0])
        if cmd is not None:
            if args.verbose:
                print(datetime.now().strftime("%Y-%m-%d,%H:%M:%S"))
            cmd.execute(client_proxy, params, proxy=True)
        else:
            if params[0] == "quit" or params[0] == "q!":
                break
            elif params[0] == "help" or params[0] == "h":
                print_help(client_info, commands)
            else:
                print(f"Unknown command: {params[0]}")


def print_help(client_info: str, commands):
    print(client_info)
    print("usage: <command> <args>\n\nUse the following commands:\n")
    print_commands(commands)
    print_color("help | h", bcolors.OKGREEN)
    print("\tPrints this help")
    print_color("quit | q!", bcolors.OKGREEN)
    print("\tExit this client")
    print("\n")


def get_parser():
    parser = argparse.ArgumentParser(prog='libra-shell')
    parser.add_argument('-u', "--url", default=TESTNET, help='Host address/name to connect to')
    parser.add_argument('-r', "--sync", action='store_true', default=False, help='If set, client will sync with validator during wallet recovery.')
    parser.add_argument('-n', "--mnemonic_file", help='File location from which to load mnemonic word for user account address/key generation.')
    parser.add_argument('-m', "--faucet_account_file", help='Path to the generated keypair for the faucet account.')
    parser.add_argument('-v', "--verbose", action='store_true', default=False, help='Verbose output.')
    parser.add_argument('-V', '--version', action='version', version=f'libra-client {version}')
    return parser


def handler(signum, frame):
    sys.exit(0)


def main():
    signal.signal(signal.SIGTERM, handler)
    signal.signal(signal.SIGINT, handler)
    # signal.signal(signal.SIGTSTP, handler)
    if os.name == 'posix':
        readline.set_history_length(1000)
    parser = get_parser()
    libra_args = parser.parse_args(sys.argv[1:])
    try:
        run_shell(libra_args)
    except Exception as err:
        report_error("some error occured", err, libra_args.verbose)


if __name__ == '__main__':
    main()