import hashlib
import hmac
import json
import requests
import sys
import time
import signal

from _config import *

# Gets the total balance of a coin
def get_coin_balance(coin):
    coin_balance_request = {
        "id": 100,
        "method": "private/get-account-summary",
        "api_key": api_key,
        "params": {
            "currency": coin
        },
        "nonce": int(time.time() * 1000)
    }

    coin_balance_response = requests.post('https://api.crypto.com/v2/private/get-account-summary',
                                          headers={'Content-type': 'application/json'},
                                          data=json.dumps(sign_request(req=coin_balance_request)))
    coin_balance_data = json.loads(coin_balance_response.content)
    coin_total_balance = coin_balance_data['result']['accounts'][0]['available']

    return coin_total_balance


# Gets the details of a coin pair
def get_pair_details(pair):
    def get_instrument(instruments, name):
        for instrument in instruments:
            if instrument['instrument_name'] == name:
                return instrument

    response = requests.get("https://api.crypto.com/v2/public/get-instruments")
    data = json.loads(response.content)
    instruments = data['result']['instruments']

    details = get_instrument(instruments, pair)

    return details


# Gets the sell price (bid) of a coin pair
def get_coin_sell_price(pair):
    get_price_response = requests.get("https://api.crypto.com/v2/public/get-ticker?instrument_name=" + pair)
    ticker = json.loads(get_price_response.content)
    coin_price = ticker['result']['data']['b']

    return coin_price

# Gets the buy price (ask) of a coin pair
def get_coin_buy_price(pair):
    get_price_response = requests.get("https://api.crypto.com/v2/public/get-ticker?instrument_name=" + pair)
    ticker = json.loads(get_price_response.content)
    coin_price = ticker['result']['data']['k']

    return coin_price


# Submits a buy order
def order_buy(pair, notional):
    # Finds the required price precision for this coin pair
    pair_data = get_pair_details(pair)
    price_precision = pair_data['price_decimals']

    # Converts the notional into a number with the correct number of decimal places
    notional = "%0.*f" % (price_precision, notional)

    order_buy_request = {
        "id": 100,
        "method": "private/create-order",
        "api_key": api_key,
        "params": {
            "instrument_name": pair,
            "side": "BUY",
            "type": "MARKET",
            "notional": notional
        },
        "nonce": int(time.time() * 1000)
    }

    order_buy_response = requests.post('https://api.crypto.com/v2/private/create-order',
                                  headers={'Content-type': 'application/json'},
                                  data=json.dumps(sign_request(req=order_buy_request)))

    return order_buy_response


# Submits a sell order
def order_sell(pair, quantity):
    # Finds the required quantity precision for this coin pair
    pair_data = get_pair_details(pair)
    quantity_precision = pair_data['quantity_decimals']

    # Converts the quantity into a number with the correct number of decimal places
    quantity = "%0.*f" % (quantity_precision, quantity)

    order_sell_request = {
        "id": 100,
        "method": "private/create-order",
        "api_key": api_key,
        "params": {
            "instrument_name": pair,
            "side": "SELL",
            "type": "MARKET",
            "quantity": quantity
        },
        "nonce": int(time.time() * 1000)
    }

    order_sell_response = requests.post('https://api.crypto.com/v2/private/create-order',
                                       headers={'Content-type': 'application/json'},
                                       data=json.dumps(sign_request(req=order_sell_request)))

    return order_sell_response


# Checks everything is in order before the bot runs
def pre_flight_checks():
    print("Performing pre-flight checks")

    # Checks whether the API key and API secret have been defined
    try:
        api_key and api_secret
    except NameError:
        print("Your API key and API secret are missing from the config file")
        sys.exit()

    # Checks whether the trading pairs have been defined, and if there is enough to begin trading
    try:
        pair_list
    except NameError:
        print("Your trading coin pairs are missing from the config file")
        sys.exit()
    else:
        if len(pair_list) < 1:
            print("You need to use at least one coin pair")
            sys.exit()

    # Checks whether the minimum sell order worth value has been defined and is valid
    try:
        min_sell_order_worth
    except NameError:
        print("Your minimum sell order worth value is missing from the config file")
        sys.exit()
    else:
        if min_sell_order_worth < 0.5:
            print("Your minimum sell order worth value must be 0.5 or greater")
            sys.exit()

    # Checks whether the minimum sell order percentage value has been defined and is valid
    try:
        min_sell_order_percentage
    except NameError:
        print("Your minimum sell order percentage value is missing from the config file")
        sys.exit()
    else:
        if min_sell_order_percentage < 1:
            print("Your minimum sell order percentage value must be 1 or greater")
            sys.exit()

    # Checks whether the buy order value has been defined and is valid
    try:
        buy_order_value
    except NameError:
        print("Your buy order value is missing from the config file")
        sys.exit()
    else:
        if buy_order_value < 0.50:
            print("Your buy order value cannot be smaller than 0.5")
            sys.exit()

    # Send a private request to test if the API key and API secret are correct
    init_request = {
        "id": 100,
        "method": "private/get-account-summary",
        "api_key": api_key,
        "params": {
            "currency": quote_currency
        },
        "nonce": int(time.time() * 1000)
    }

    init_response = requests.post('https://api.crypto.com/v2/private/get-account-summary',
                                  headers={'Content-type': 'application/json'},
                                  data=json.dumps(sign_request(req=init_request)))
    init_status = init_response.status_code

    if init_status == 200:
        # The bot can connect to the account, has been started, and is waiting to be called
        print("Pre-flight checks successful")

    else:
        # Could not connect to the account
        print("Could not connect to your account. Please ensure the API key and API secret are correct and have the right privileges")
        sys.exit()


# Signs private requests
def sign_request(req):
    param_string = ''

    if 'params' in req:
        for key in sorted(req['params']):
            param_string += key
            param_string += str(req['params'][key])

    sig_payload = req['method'] + str(req['id']) + req['api_key'] + param_string + str(req['nonce'])

    req['sig'] = hmac.new(
        bytes(str(api_secret), 'utf-8'),
        msg=bytes(sig_payload, 'utf-8'),
        digestmod=hashlib.sha256
    ).hexdigest()

    return req

# Kills PieBot gracefully
class GracefulKiller:
  kill_now = False

  def __init__(self):
    signal.signal(signal.SIGINT, self.exit_gracefully)
    signal.signal(signal.SIGTERM, self.exit_gracefully)

  def exit_gracefully(self, *args):
    self.kill_now = True
    
    print("")
    print("Shutting down")