import sys
from getpass import getpass
from inspect import signature

import connection
from connection import client_request
from debug import debug
from run_client import allowed_commands, fake_loading_bar
from util import my_tabulate

exiting = False


def login(username=None, password=None):
    if connection.session_id is not None:
        fake_loading_bar('Signing out', duration=0.7)
        connection.session_id = None

    if username is None:
        username = input('Username: ')

    if password is None:
        if sys.stdin.isatty():
            password = getpass('Password: ')
        else:
            password = input('Password: ')

    fake_loading_bar('Signing in', duration=2.3)
    response = client_request('login', {"username": username, "password": password})
    success = 'session_id' in response
    if success:
        connection.session_id = response['session_id']
        print('Login successful.')
    else:
        if 'error_message' in response:
            print('Login failed with message:', response['error_message'])
        else:
            print('Login failed.')


def register(username=None, game_key='', password=None, retype_pw=None):
    if connection.session_id is not None:
        connection.session_id = None
        fake_loading_bar('Signing out', duration=0.7)

    if username is None:
        username = input('Username: ')

    if password is None:
        if sys.stdin.isatty():
            password = getpass('New password: ')
            retype_pw = getpass('Retype password: ')
        else:
            password = input('New password: ')
            retype_pw = input('Retype password: ')
        if password != retype_pw:
            print('Passwords do not match.')
            return
    elif retype_pw is None:
        if sys.stdin.isatty():
            retype_pw = getpass('Retype password: ')
        else:
            retype_pw = input('Retype password: ')
        if password != retype_pw:
            print('Passwords do not match.')
            return

    if not debug:
        if game_key == '':
            print('Entering a game key will provide you with some starting money and other useful stuff.')
            game_key = input('Game key (leave empty if you don\'t have one): ')

    fake_loading_bar('Validating Registration', duration=5.2)
    if game_key != '':
        fake_loading_bar('Validating Game Key', duration=0.4)
    response = client_request('register', {"username": username, "password": password, "game_key": game_key})

    if 'error_message' in response:
        print('Registration failed with message:', response['error_message'])


def cancel_order(order_no=None):
    if order_no is None:
        order_no = input('Order No.: ')

    fake_loading_bar('Validating Request', duration=0.6)
    response = client_request('cancel_order', {"session_id": connection.session_id, "order_id": order_no})

    if 'error_message' in response:
        print('Order cancelling failed with message:', response['error_message'])


def change_pw(password=None, retype_pw=None):
    if password != retype_pw:
        password = None
    if password is None:
        if sys.stdin.isatty():
            password = getpass('New password: ')
            retype_pw = getpass('Retype password: ')
        else:
            password = input('New password: ')
            retype_pw = input('Retype password: ')
        if password != retype_pw:
            print('Passwords do not match.')
            return
    elif retype_pw is None:
        if sys.stdin.isatty():
            retype_pw = getpass('Retype password: ')
        else:
            retype_pw = input('Retype password: ')
        if password != retype_pw:
            print('Passwords do not match.')
            return

    fake_loading_bar('Validating password', duration=1.2)
    fake_loading_bar('Changing password', duration=5.2)
    response = client_request('change_password', {"session_id": connection.session_id, "password": password})

    if 'error_message' in response:
        print('Changing password failed with message:', response['error_message'])

    fake_loading_bar('Signing out', duration=0.7)
    connection.session_id = None


# noinspection PyShadowingBuiltins
def help():
    print('Allowed commands:')
    command_table = []
    for cmd in allowed_commands:
        this_module = sys.modules[__name__]
        method = getattr(this_module, cmd)
        params = signature(method).parameters
        command_table.append([cmd] + [p for p in params])

    print(my_tabulate(command_table, tablefmt='pipe', headers=['command',
                                                               'param 1',
                                                               'param 2',
                                                               'param 3',
                                                               'param 4',
                                                               'param 5',
                                                               ]))
    print('NOTE:')
    print('  All parameters for all commands are optional!')
    print('  Commands can be combined in one line with ; between them.')
    print('  Parameters are separated with whitespace.')
    print('  Use . as a decimal separator.')
    print('  Use 0/1 for boolean parameters (other strings might work).')


def depot():
    fake_loading_bar('Loading data', duration=1.3)
    response = client_request('depot', {"session_id": connection.session_id})
    success = 'data' in response and 'own_wealth' in response
    if success:
        data = response['data']
        for row in data:
            row.append(row[1] * row[2])
        print(my_tabulate(data,
                          headers=['Object', 'Amount', 'Course', 'Bid', 'Ask', 'Est. Value'],
                          tablefmt="pipe"))
        print('This corresponds to a wealth of roughly', response['own_wealth'])
    else:
        if 'error_message' in response:
            print('Depot access failed with message:', response['error_message'])
        else:
            print('Depot access failed.')


def leaderboard():
    fake_loading_bar('Loading data', duration=1.3)
    response = client_request('leaderboard', {})
    success = 'data' in response
    if success:
        print(my_tabulate(response['data'], headers=['Trader', 'Wealth'], tablefmt="pipe"))
        # print('Remember that the goal is to be as rich as possible, not to be richer than other traders!')
    else:
        if 'error_message' in response:
            print('Leaderboard access failed with message:', response['error_message'])
        else:
            print('Leaderboard access failed.')


def activate_key(key=''):
    if key == '':
        print('Entering a game key may get you some money or other useful stuff.')
        key = input('Key: ')

    if key == '':
        print('Invalid key.')

    fake_loading_bar('Validating Key', duration=0.4)
    response = client_request('activate_key', {"session_id": connection.session_id, 'key': key})
    if 'error_message' in response:
        print('Key activation failed with message:', response['error_message'])


def yn_dialog(msg):
    while True:
        result = input(msg + ' [y/n]: ')
        if result == 'y':
            return True
        if result == 'n':
            return False
        print('Type in \'y\' or \'n\'!')


def buy(obj_name=None, amount=None, limit='', stop_loss='', expiry=None):
    if obj_name is None:  # TODO list some available objects
        obj_name = input('Name of object to buy: ')
    if amount is None:
        amount = input('Amount: ')
    if limit == '':
        set_limit = yn_dialog('Do you want to place a limit?')
        if set_limit:
            limit = input('Limit: ')
            stop_loss = yn_dialog('Is this a stop-loss limit?')
        else:
            limit = None
            stop_loss = None
    if limit != '' and stop_loss == '':
        stop_loss = yn_dialog('Is this a stop-loss limit?')
    if limit is not None and \
            float(limit) <= 0 and \
            input(input('Are you sure you want to use such a low limit (limit=' + str(
                limit) + ')? (type in "yes" or something else):') != 'yes'):
        print('Order was not placed.')
        return

    if expiry is None:
        expiry = input('Time until order expires (minutes, default 43200):')
        if expiry == '':
            expiry = 43200
    try:
        expiry = float(expiry)
    except ValueError:
        print('Invalid expiration time.')
        return

    fake_loading_bar('Loading Data', duration=1.3)
    response = client_request('order', {"buy": True,
                                        "session_id": connection.session_id,
                                        "amount": amount,
                                        "ownable": obj_name,
                                        "limit": limit,
                                        "stop_loss": stop_loss,
                                        "time_until_expiration": expiry})
    if 'error_message' in response:
        print('Order placement failed with message:', response['error_message'])
    else:
        print('You might want to use the `trades` or `depot` commands',
              'to see if the order has been executed already.')


def sell(obj_name=None, amount=None, limit='', stop_loss='', expiry=None):
    if obj_name is None:  # TODO list some available objects
        obj_name = input('Name of object to sell: ')
    if amount is None:
        amount = input('Amount: ')
    if limit == '':
        set_limit = yn_dialog('Do you want to place a limit?')
        if set_limit:
            limit = input('Limit: ')
            stop_loss = yn_dialog('Is this a stop-loss limit?')
        else:
            limit = None
            stop_loss = None
    if limit != '' and stop_loss == '':
        stop_loss = yn_dialog('Is this a stop-loss limit?')
    if limit is not None and \
            float(limit) <= 0 and \
            input('Are you sure you want to use such a low limit (limit=' + str(
                limit) + ')? (type in "yes" or something else):') != 'yes':
        print('Order was not placed.')
        return

    if expiry is None:
        expiry = input('Time until order expires (minutes, default 43200):')
        if expiry == '':
            expiry = 43200
    try:
        expiry = float(expiry)
    except ValueError:
        print('Invalid expiration time.')
        return

    fake_loading_bar('Loading Data', duration=1.3)
    response = client_request('order', {"buy": False,
                                        "session_id": connection.session_id,
                                        "amount": amount,
                                        "ownable": obj_name,
                                        "limit": limit,
                                        "stop_loss": stop_loss,
                                        "time_until_expiration": expiry})
    if 'error_message' in response:
        print('Order placement failed with message:', response['error_message'])
    else:
        print('You might want to use the `trades` or `depot` commands',
              'to see if the order has been executed already.')


def orders():
    fake_loading_bar('Loading Data', duration=0.9)
    response = client_request('orders', {"session_id": connection.session_id})
    success = 'data' in response
    if success:
        print(my_tabulate(response['data'],
                          headers=['Buy?', 'Name', 'Size', 'Limit', 'stop-loss', 'Expires', 'No.'],
                          tablefmt="pipe"))
    else:
        if 'error_message' in response:
            print('Order access failed with message:', response['error_message'])
        else:
            print('Order access failed.')


def orders_on(obj_name=None):
    if obj_name is None:  # TODO list some available objects
        obj_name = input('Name of object to check: ')
    fake_loading_bar('Loading Data', duration=2.3)
    response = client_request('orders_on', {"session_id": connection.session_id, "ownable": obj_name})
    success = 'data' in response
    if success:
        print(my_tabulate(response['data'],
                          headers=['My', 'Buy?', 'Name', 'Size', 'Limit', 'Expires', 'No.'],
                          tablefmt="pipe"))
    else:
        if 'error_message' in response:
            print('Order access failed with message:', response['error_message'])
        else:
            print('Order access failed.')


def gift(username=None, obj_name=None, amount=None):
    if username is None:
        username = input('Username of recipient: ')
    if obj_name is None:
        obj_name = input('Name of object to give: ')
    if amount is None:
        amount = input('How many?: ')
    fake_loading_bar('Sending Gift', duration=4.2)
    response = client_request('gift',
                              {"session_id": connection.session_id,
                               "username": username,
                               "object_name": obj_name,
                               "amount": amount})
    if 'error_message' in response:
        print('Order access failed with message:', response['error_message'])
    elif 'message' in response:
        print(response['message'])


def news():
    fake_loading_bar('Loading Data', duration=0.76)
    response = client_request('news', {})
    success = 'data' in response
    if success:
        print(my_tabulate(response['data'],
                          headers=['Date', 'Title'],
                          tablefmt="pipe"))
    else:
        if 'error_message' in response:
            print('News access failed with message:', response['error_message'])
        else:
            print('News access failed.')


def tradables():
    fake_loading_bar('Loading Data', duration=12.4)
    response = client_request('tradables', {})
    success = 'data' in response
    if success:
        print(my_tabulate(response['data'],
                          headers=['Name', 'Course', 'Market Cap.'],
                          tablefmt="pipe"))
        world_wealth = 0
        for row in response['data']:
            if row[2] is not None:
                world_wealth += row[2]
        print('Estimated worldwide wealth:', world_wealth)

    else:
        if 'error_message' in response:
            print('Data access failed with message:', response['error_message'])
        else:
            print('Data access failed.')


def trades_on(obj_name=None, limit=5):
    limit = float(limit)
    if obj_name is None:  # TODO list some available objects
        obj_name = input('Name of object to check: ')
    fake_loading_bar('Loading Data', duration=1.3)
    response = client_request('trades_on', {"session_id": connection.session_id,
                                            "ownable": obj_name,
                                            "limit": limit})
    success = 'data' in response
    if success:
        print(my_tabulate(response['data'],
                          headers=['Time', 'Volume', 'Price'],
                          tablefmt="pipe"))
    else:
        if 'error_message' in response:
            print('Trades access failed with message:', response['error_message'])
        else:
            print('Trades access failed.')


def trades(limit=10):
    limit = float(limit)
    fake_loading_bar('Loading Data', duration=2.7)
    response = client_request('trades', {"session_id": connection.session_id,
                                         "limit": limit})
    success = 'data' in response
    if success:
        print(my_tabulate(response['data'],
                          headers=['Buy?', 'Name', 'Volume', 'Price', 'Time'],
                          tablefmt="pipe"))
    else:
        if 'error_message' in response:
            print('Trades access failed with message:', response['error_message'])
        else:
            print('Trades access failed.')


# noinspection PyShadowingBuiltins
def exit():
    global exiting
    exiting = True