#!/usr/bin/env python

from __future__ import print_function

import sys
import argparse
import json
import datetime

from concurrent import futures

from moneywagon import (
    CurrentPrice, HistoricalPrice, AddressBalance, get_address_balance,
    get_historical_transactions, get_block, get_unspent_outputs,
    generate_keypair, sweep
)
from moneywagon.wallet import fetch_wallet_balances

parser = argparse.ArgumentParser() #version='1.0.2')

subparsers = parser.add_subparsers(help='commands', dest="subparser_name")

x = subparsers.add_parser('current-price', help='Get current price of a crypto/fiat pair.')
x.add_argument('crypto', action='store', help='Cryptocurrency symbol')
x.add_argument('fiat', action='store', help='Fiat currency symbol')
x.add_argument('--verbose', action='store_true', help='Include extra output')
x.add_argument('--random-service', action='store_true', help='Use a random source')

x = subparsers.add_parser('generate-keypair', help='Generate Private/Public key pair')
x.add_argument('crypto', action='store', help='Cryptocurrency symbol')
x.add_argument('seed', action='store', help='Random seed string.'),
x.add_argument('--password', action='store', help='Encrypt private key with Bip38 password.')
x.add_argument('--verbose', action='store_true', help='Include extra output')

x = subparsers.add_parser('historical-price', help='Get price of a crypto/fiat pair at a point in time.')
x.add_argument('crypto', action='store', help='Cryptocurrency symbol')
x.add_argument('fiat', action='store', help='Fiat currency symbol')
x.add_argument('at_time', action='store', help='Time when to get the price. e.g. 2014-04-03')
x.add_argument('--verbose', action='store_true', help='Include extra output')

x = subparsers.add_parser('address-balance', help='Get total amount of coin in wallet.')
x.add_argument('crypto', action='store', help='Cryptocurrency symbol')
x.add_argument('address', action='store', help='Wallet address')
x.add_argument('--paranoid', action='store', help='How many services to use when cross-checking')
x.add_argument('--verbose', action='store_true', help='Include extra output')
x.add_argument('--random-service', action='store_true', help='Use a random source')

x = subparsers.add_parser('get-block', help='Get block by either height, hash or by latest.')
x.add_argument('crypto', action='store', help='Cryptocurrency symbol')
x.add_argument('--block_number', action='store', help='Get block by block number')
x.add_argument('--block_hash', action='store', help='Get block by block hash')
x.add_argument('--latest', action='store_true', help='Get the latest block.')
x.add_argument('--paranoid', action='store', help='How many services to use when cross-checking')
x.add_argument('--verbose', action='store_true', help='Include extra output')
x.add_argument('--random-service', action='store_true', help='Use a random source')

x = subparsers.add_parser('historical-transactions', help='Get list of all transactions for this address.')
x.add_argument('crypto', action='store', help='Cryptocurrency symbol')
x.add_argument('address', action='store', help='Wallet address')
x.add_argument('--paranoid', action='store', help='How many services to use when cross-checking')
x.add_argument('--verbose', action='store_true', help='Include extra output')
x.add_argument('--random-service', action='store_true', help='Use a random source')

x = subparsers.add_parser('sweep', help='Sweep funds from a private key to another address')
x.add_argument('crypto', action='store', help='Cryptocurrency symbol')
x.add_argument('private_key', action='store', help='Private key to draw funds from')
x.add_argument('to_address', action='store', help='Address to send funds to.')
x.add_argument('--password', action='store', help='Decrypt private key with Bip38 password.')
x.add_argument('--fee', action='store', help='Fee to use for tx (in satoshi). Defaults to $0.02', default=None)
x.add_argument('--paranoid', action='store', help='How many services to use when cross-checking')
x.add_argument('--verbose', action='store_true', help='Include extra output')
x.add_argument('--random-service', action='store_true', help='Use a random source')

x = subparsers.add_parser('unspent-outputs', help='Get list of unspent outputs for this address.')
x.add_argument('crypto', action='store', help='Cryptocurrency symbol')
x.add_argument('address', action='store', help='Wallet address')
x.add_argument('--paranoid', action='store', help='How many services to use when cross-checking')
x.add_argument('--verbose', action='store_true', help='Include extra output')
x.add_argument('--random-service', action='store_true', help='Use a random source')

x = subparsers.add_parser('wallet-balance', help='Get current value in fiat of a set of crypo addresses.')
x.add_argument('wallet', type=argparse.FileType('r'), default=sys.stdin, help='Wallet file')
x.add_argument('fiat', action='store', help='Fiat currency')
x.add_argument('--paranoid', action='store', help='How many services to use when cross-checking')
x.add_argument('--verbose', action='store_true', help='Include extra output')
x.add_argument('--async', action='store_true', help='Fetch prices and balances asynchronously')
x.add_argument('--random-service', action='store_true', help='Use random sources')

argz = parser.parse_args()

def datetime_to_iso(obj):
    """
    Python's default json encoder will blow up when it encounters datetime objects.
    So this work around is needed in order to just handle making datetime
    objects into iso8601 string format.
    """
    if isinstance(obj, datetime.datetime):
        serial = obj.isoformat()
        return serial
    raise TypeError("Type not serializable")

modes = {
    'random': argz.random_service if hasattr(argz, "random_service") else False,
    'paranoid': int(argz.paranoid or 1) if hasattr(argz, 'paranoid') else 1,
    'verbose': argz.verbose
}

if argz.subparser_name == 'current-price':
    price, source = CurrentPrice(verbose=argz.verbose).get(argz.crypto, argz.fiat)
    print(price)

elif argz.subparser_name == 'generate-keypair':
    if argz.seed == '-':
        seed =  "".join(sys.stdin)
    else:
        seed = argz.seed

    print(json.dumps(generate_keypair(argz.crypto, seed, password=argz.password)))

elif argz.subparser_name == 'historical-price':
    price, source, date = HistoricalPrice(verbose=argz.verbose).get(argz.crypto, argz.fiat, argz.at_time)
    print(price, source, date)

elif argz.subparser_name == 'address-balance':
    print(get_address_balance(argz.crypto, argz.address, **modes))

elif argz.subparser_name == 'get-block':
    print(json.dumps(get_block(
        argz.crypto, block_number=argz.block_number or '', block_hash=argz.block_hash or '',
        latest=argz.latest or False, modes=modes
    ), default=datetime_to_iso))

elif argz.subparser_name == 'historical-transactions':
    print(json.dumps(get_historical_transactions(
        argz.crypto, argz.address, **modes
    ), default=datetime_to_iso))

elif argz.subparser_name == "unspent-outputs":
    print(json.dumps(get_unspent_outputs(
        argz.crypto, argz.address, **modes
    ), default=datetime_to_iso))

elif argz.subparser_name == 'sweep':
    print(sweep(argz.crypto, argz.private_key, argz.to_address, argz.fee, **modes))

elif argz.subparser_name == 'wallet-balance':
    wallets = [
        (x.split(",")[0], x.split(",")[1].strip())
        for x in argz.wallet.readlines()
        if not x.startswith("#")
    ]

    if argz.async:
        modes['async'] = True

    fiat = argz.fiat.upper()

    cumm_amount = 0
    for d in fetch_wallet_balances(wallets, fiat, **modes):
        cumm_amount += d['fiat_value']
        print(
            "%s (%.2f %s) == %s x %s (%s)" % (
                d['crypto'], d['fiat_value'], fiat,
                d['crypto_value'], d['conversion_price'], d['price_source'],
            )
        )
    print("Total amount of all crypto: %.2f %s" % (cumm_amount, fiat))
