import argparse
import logging
import sys
from datetime import timedelta
from timeit import default_timer as timer

from prettytable import PrettyTable

from nectar import Blurt, Hive, Steem
from nectar.block import Block
from nectar.blockchain import Blockchain
from nectar.nodelist import NodeList
from nectar.utils import parse_time

log = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)


def parse_args(args=None):
    d = "Show op type stats for either hive, blurt or steem."
    parser = argparse.ArgumentParser(description=d)
    parser.add_argument(
        "blockchain",
        type=str,
        nargs="?",
        default=sys.stdin,
        help="Blockchain (hive, blurt or steem)",
    )
    return parser.parse_args(args)


def main(args=None):
    args = parse_args(args)
    blockchain = args.blockchain

    nodelist = NodeList()
    nodelist.update_nodes(weights={"block": 1})

    if blockchain == "hive" or blockchain is None:
        max_batch_size = 50
        threading = False
        thread_num = 16
        block_debug = 1000

        nodes = nodelist.get_hive_nodes()
        blk_inst = Hive(node=nodes, num_retries=3, num_retries_call=3, timeout=30)
    elif blockchain == "blurt":
        max_batch_size = None
        threading = False
        thread_num = 8
        block_debug = 20
        nodes = [
            "https://rpc.blurt.buzz/",
            "https://api.blurt.blog",
            "https://rpc.blurtworld.com",
            "https://rpc.blurtworld.com",
        ]
        blk_inst = Blurt(node=nodes, num_retries=3, num_retries_call=3, timeout=30)
    elif blockchain == "steem":
        max_batch_size = 50
        threading = False
        thread_num = 16
        block_debug = 1000
        nodes = nodelist.get_steem_nodes()
        blk_inst = Steem(node=nodes, num_retries=3, num_retries_call=3, timeout=30)
    else:
        raise Exception("Wrong parameter, can be hive, blurt or steem")
    print(blk_inst)
    block_count = 0
    total_ops = 0
    total_trx = 0
    duration_s = 60 * 60 * 1
    blocksperday = int(duration_s / 3)

    blockchain = Blockchain(
        blockchain_instance=blk_inst,
    )
    current_block_num = blockchain.get_current_block_num()
    last_block_id = current_block_num - blocksperday

    last_block = Block(last_block_id, blockchain_instance=blk_inst)

    stopTime = last_block.time() + timedelta(seconds=duration_s)

    start = timer()
    op_stats = {}
    for entry in blockchain.blocks(
        start=last_block_id,
        max_batch_size=max_batch_size,
        threading=threading,
        thread_num=thread_num,
    ):
        if "block" in entry:
            block_time = parse_time(entry["block"]["timestamp"])
        else:
            block_time = entry["timestamp"]
        if block_time > stopTime:
            break
        block_count += 1
        if "block" in entry:
            trxs = entry["block"]["transactions"]
        else:
            trxs = entry["transactions"]
        for tx in trxs:
            total_trx += 1
            for op in tx["operations"]:
                if "_operation" in op["type"]:
                    op_type = op["type"][:-10]
                else:
                    op_type = op["type"]
                if op_type in op_stats:
                    op_stats[op_type] += 1
                else:
                    op_stats[op_type] = 1
                total_ops += 1

        ops_per_day = total_ops / block_count * blocksperday
        if block_count % (block_debug) == 0:
            print(
                "%d blocks remaining... estimated ops per day: %.1f"
                % (blocksperday - block_count, ops_per_day)
            )

    duration = timer() - start
    t = PrettyTable(["Type", "Count", "percentage"])
    t.align = "l"
    op_list = []
    for o in op_stats:
        op_list.append({"type": o, "n": op_stats[o], "perc": op_stats[o] / total_ops * 100})
    op_list_sorted = sorted(op_list, key=lambda x: x["n"], reverse=True)
    for op in op_list_sorted:
        t.add_row([op["type"], op["n"], "%.2f %%" % op["perc"]])
    print(t)


if __name__ == "__main__":
    sys.exit(main())
