#!/usr/bin/env python
"""

Join and Quit RNode

"""
import logging
import argparse
import sys
import os
import getpass
import json

cmd_dir = os.path.dirname(os.path.realpath(__file__))
fusion_dir = os.path.dirname(cmd_dir)
sys.path.append(fusion_dir)

from cpc_fusion import Web3
from cpc_fusion.default_contracts import RNode, Campaign
from cpc_fusion.cmd.version import get_version_parser, get_version
from cpc_fusion.cmd.account import get_account_parser
from cpc_fusion.cmd.view_func import builder_view_func_parser

logging.basicConfig(stream=sys.stdout, level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
log = logging.getLogger()

cf = Web3(Web3.HTTPProvider('https://civilian.cpchain.io'))
rnode = RNode(cf)
campaign = Campaign(cf)

def join_rnode(args):
    rnode.join(keystorePath=args.keystore)

def quit_rnode(args):
    rnode.quit(keystorePath=args.keystore)

def check_rnode(args):
    if args.addr:
        if rnode.is_rnode(args.addr):
            log.info('This address is RNode')
        else:
            log.info('This address is not RNode')
        return
    log.info('RNode version: %d', rnode.supported_version)
    log.info('RNode locked period: %d min', rnode.period / 60)
    log.info('RNode threshold: %d CPC', cf.fromWei(rnode.threshold, 'ether'))
    log.info('RNodes count: %d', rnode.rnodes_num)


def check_campaign(args):
    log.info('Campaign version: %d', campaign.supported_version)
    log.info('Campaign term-idx: %d', campaign.term_idx)
    log.info('Campaign view-len: %d', campaign.view_len)
    log.info('Campaign term-len: %d', campaign.term_len)
    log.info('Campaign min-noc: %d', campaign.min_noc)
    log.info('Campaign max-noc: %d', campaign.max_noc)


def _get_instance(cf, args, has_address=True):
    """ Get instance
    """
    with open(args.abi, 'r') as fr:
        contract_data = json.load(fr)
    if not has_address:
        contract = cf.cpc.contract(
            abi=contract_data['abi'], bytecode=contract_data['bytecode'])
    else:
        contract = cf.cpc.contract(address=args.address, abi=contract_data['abi'])
    return contract, contract_data['contractName']


def deploy(args):
    log.info('Start to deploy contract')
    log.info('ABI: %s, keystore: %s, endpint: %s, chainID %d', args.abi, args.keystore, args.endpoint, args.chainID)

    cf = Web3(Web3.HTTPProvider(args.endpoint))
    contract, contract_name = _get_instance(cf, args, False)

    # build tx
    keystorePath = args.keystore
    with open(keystorePath, 'r') as fr:
        ks = json.load(fr)
        addr = cf.toChecksumAddress(ks['address'])
    gas_price = cf.cpc.gasPrice
    nonce = cf.cpc.getTransactionCount(addr)
    estimated_gas = contract.constructor().estimateGas()
    tx = contract.constructor().buildTransaction({
        'gasPrice': gas_price,
        "nonce": nonce,
        "gas": estimated_gas,
        "from": addr,
        "value": cf.toWei(0, 'ether'),
        "type": 0,
        "chainId": args.chainID
    })

    # send tx
    password = getpass.getpass("Please input your password:")
    decrypted_key = cf.cpc.account.decrypt(ks, password)
    password = ""
    signed_txn = cf.cpc.account.signTransaction(tx, decrypted_key)
    tx_hash = cf.cpc.sendRawTransaction(signed_txn.rawTransaction)
            
    # get tx receipt to get contract address
    tx_receipt = cf.cpc.waitForTransactionReceipt(tx_hash)
    address = tx_receipt['contractAddress']
    log.info(f'{contract_name} Address: {address}, Block: {tx_receipt["blockNumber"]}')
    return address


def get_configs(args):
    """
    遍历 ABI，获取所有的参数，并进行打印
    """
    with open(args.abi, 'r') as fr:
        contract_data = json.load(fr)
    abi = contract_data['abi']

    cf = Web3(Web3.HTTPProvider(args.endpoint))
    instance, name = _get_instance(cf, args)
    log.info(f'{name} contract')
    # 遍历 ABI，获取所有的可获取的参数变量
    log.info('-' * 10 +  'Configs' + '-' * 10)
    constants = [i for i in abi if i.get('constant') == True and i['type'] == 'function' and len(i['inputs']) == 0]
    for constant in constants:
        name = constant["name"]
        value = instance.functions[name]().call()
        log.info(f'{name}: {value}')


def call_func(args):
    with open(args.abi, 'r') as fr:
        contract_data = json.load(fr)
    abi = contract_data['abi']
    cf = Web3(Web3.HTTPProvider(args.endpoint))
    instance, name = _get_instance(cf, args)
    log.info(f'{name} contract')

    keystorePath = args.keystore
    with open(keystorePath, 'r') as fr:
        ks = json.load(fr)
        addr = cf.toChecksumAddress(ks['address'])

    name = args.function
    value = args.parameters

    # 获取指定的设置信息
    constants = [i for i in abi if i.get('name') == name]
    if len(constants) == 0:
        log.error(f"Can't find this configuration: {name}")
        return
    constant = constants[0]
    if len(value.split(',')) != len(constant['inputs']):
        log.error(f'Your input {len(value.split(","))} parameters, not equal to {len(constant["inputs"])}')
        return
    inputs = []
    for i in range(len(constant['inputs'])):
        target = constant['inputs'][i]
        val = value.split(',')[i]
        if target['type'] in ['uint', 'uint256']:
            val = int(val)
        inputs.append(val)
    estimated_gas = 300000 # instance.functions[name](*inputs).estimateGas()
    gas_price = cf.cpc.gasPrice
    nonce = cf.cpc.getTransactionCount(addr)
    tx = instance.functions[name](*inputs).buildTransaction({
        'gasPrice': gas_price,
        "nonce": nonce,
        "gas": estimated_gas,
        "from": addr,
        "value": cf.toWei(args.value, 'ether'),
        "type": 0,
        "chainId": args.chainID
    })
    # send tx
    password = getpass.getpass("Please input your password:")
    decrypted_key = cf.cpc.account.decrypt(ks, password)
    password = ""
    signed_txn = cf.cpc.account.signTransaction(tx, decrypted_key)
    tx_hash = cf.cpc.sendRawTransaction(signed_txn.rawTransaction)
            
    # get tx receipt to get contract address
    receipt = cf.cpc.waitForTransactionReceipt(tx_hash)
    if receipt.status == 0:
        print(receipt)
        log.info('Sorry, failed.')
    else:
        log.info('Success')


parser = argparse.ArgumentParser()

parser.add_argument('-v', '--version', action='version', version=get_version())

subparsers = parser.add_subparsers(help="sub-command help")

# account parser
get_account_parser(subparsers)

# RNode
rnode_parser = subparsers.add_parser('rnode', help="RNode contract")
rnode_sub_parser = rnode_parser.add_subparsers(help='rnode sub-command')

query_rnode_parser = rnode_sub_parser.add_parser('check', help='check parameters for rnode contracts')
query_rnode_parser.add_argument('--addr', type=str, help='Check whether this address is RNode')
query_rnode_parser.set_defaults(func=check_rnode)

join_rnode_parser = rnode_sub_parser.add_parser('join', help='Join RNode')
join_rnode_parser.add_argument('--keystore', type=str, help='Path of the keystore file')
join_rnode_parser.set_defaults(func=join_rnode)

quit_rnode_parser = rnode_sub_parser.add_parser('quit', help='Quit RNode')
quit_rnode_parser.add_argument('--keystore', type=str, help='Path of the keystore file')
quit_rnode_parser.set_defaults(func=quit_rnode)

# Campaign
campaign_parser = subparsers.add_parser('campaign', help="Campaign contract")
campaign_sub_parser = campaign_parser.add_subparsers(help='campaign sub-command')

check_campaign_parser = campaign_sub_parser.add_parser('check', help='check parameters for campaign contracts')
check_campaign_parser.set_defaults(func=check_campaign)

# Deploy
deploy_parser = subparsers.add_parser('deploy', help="Deploy contract")
deploy_parser.add_argument('--keystore', type=str, help='Path of the keystore file', required=True)
deploy_parser.add_argument('--abi', type=str, help='Path of the keystore file', required=True)
deploy_parser.add_argument('--endpoint', type=str, help='RPC endpoint of your node', required=False, default="https://civilian.cpchain.io")
deploy_parser.add_argument('--chainID', type=int, help="ChainID of the chain, mainnet: 337(Default), testnet: 41", default=337)
deploy_parser.set_defaults(func=deploy)

# Get configs
get_conf_parser = subparsers.add_parser('get-configs', help="Get the config of a smart contract")
get_conf_parser.add_argument('--address', type=str, help='Address of this contract', required=True)
get_conf_parser.add_argument('--abi', type=str, help='Path of the ABI file', required=True)
get_conf_parser.add_argument('--endpoint', type=str, help='RPC endpoint of your node', required=False, default="https://civilian.cpchain.io")
get_conf_parser.add_argument('--chainID', type=int, help="ChainID of the chain, mainnet: 337(Default), testnet: 41", default=337)
get_conf_parser.set_defaults(func=get_configs)

# Set configs
call_func_parser = subparsers.add_parser('call-func', help="Call smart contract")
call_func_parser.add_argument('--keystore', type=str, help='Path of the keystore file', required=True)
call_func_parser.add_argument('--address', type=str, help='Address of this contract', required=True)
call_func_parser.add_argument('--abi', type=str, help='Path of the ABI file', required=True)
call_func_parser.add_argument('--function', type=str, help='Function name of the parameter', required=True)
call_func_parser.add_argument('--parameters', type=str, help='Value of the parameter(split by ,)', required=True)
call_func_parser.add_argument('--value', type=float, help="The value(CPC) of the transaction", default=0, required=False)
call_func_parser.add_argument('--endpoint', type=str, help='RPC endpoint of your node', required=False, default="https://civilian.cpchain.io")
call_func_parser.add_argument('--chainID', type=int, help="ChainID of the chain, mainnet: 337(Default), testnet: 41", default=337)
call_func_parser.set_defaults(func=call_func)

# view funcs
builder_view_func_parser(subparsers)

# version parser
get_version_parser(subparsers)

args = parser.parse_args()
args.func(args)
