import shutil
import os
from behave import given as Given, when as When, then as Then
from sure import expect, ensure

from pageObjects.AccountConnectionWizard import AccountConnectionWizard
from pageObjects.SyncConnectionWizard import SyncConnectionWizard
from pageObjects.AccountSetting import AccountSetting
from pageObjects.Toolbar import Toolbar
from pageObjects.EnterPassword import EnterPassword
from helpers.SetupClientHelper import (
    start_client,
    setup_client,
    substitute_inline_codes,
    get_client_details,
    generate_account_config,
    get_resource_path,
)
from helpers.SyncHelper import (
    wait_for_initial_sync_to_complete,
    listen_sync_status_for_item,
)
from helpers.UserHelper import get_displayname_for_user, get_password_for_user
from helpers.ConfigHelper import get_config
from helpers.TableParser import table_rows_hash
from helpers.AppHelper import close_and_kill_app


@Given('the user has started the client')
def step(context):
    start_client()


@When('the user adds the following user credentials:')
def step(context):
    account_details = get_client_details(context)
    set_config('syncConnectionName', get_displayname_for_user(account_details['user']))
    AccountConnectionWizard.add_user_credentials(
        account_details['user'], account_details['password']
    )


@Then('"{username}" account should be added')
def step(context, username):
    username = substitute_inline_codes(username)
    expect(Toolbar.account_exists(username)).to.be.true


@Then('"{username}" account should not be displayed')
def step(context, username):
    username = substitute_inline_codes(username)
    expect(Toolbar.account_exists(username)).to.be.false


@Given('user "{username}" has set up a client with default settings')
def step(context, username):
    password = get_password_for_user(username)
    setup_client(username)
    enter_password = EnterPassword()
    enter_password.accept_certificate()

    enter_password.login_after_setup(username, password)

    # wait for files to sync
    wait_for_initial_sync_to_complete(get_resource_path('/', username))
    Toolbar.wait_toolbar_enabled()


@Given('the user has set up the following accounts with default settings:')
def step(context):
    users = []
    for row in context.table:
        users.append(row[0])
    sync_paths = generate_account_config(users)
    start_client()
    # accept certificate for each user
    for idx, _ in enumerate(users):
        enter_password = EnterPassword()
        enter_password.accept_certificate()

    for idx, _ in enumerate(sync_paths.values()):
        # login from last dialog
        enter_password = EnterPassword()
        username = enter_password.get_username()
        password = get_password_for_user(username)
        listen_sync_status_for_item(sync_paths[username])
        enter_password.login_after_setup(username, password)
        # wait for files to sync
        wait_for_initial_sync_to_complete(sync_paths[username])
    Toolbar.wait_toolbar_enabled()


@When('the user starts the client')
def step(context):
    start_client()


@When('the user opens the add-account dialog')
def step(context):
    Toolbar.open_new_account_setup()


@When('the user adds the following account:')
def step(context):
    data = table_rows_hash(context.table)
    account_details = get_client_details(data)
    AccountConnectionWizard.add_account(account_details)
    # # wait for files to sync
    wait_for_initial_sync_to_complete(get_resource_path('/', account_details['user']))
    Toolbar.wait_toolbar_enabled()


@Given('the user has entered the following account information:')
def step(context):
    data = table_rows_hash(context.table)
    account_details = get_client_details(data)
    AccountConnectionWizard.add_account_information(account_details)


@When('the user "{username}" logs out using the client-UI')
def step(context, username):
    AccountSetting.logout()


@Then('user "{username}" should be signed out')
def step(context, username):
    user_signed_out = AccountSetting.is_user_signed_out()

    with ensure('User "{0}" should be signed out, but is still signed in', username):
        user_signed_out.should.be.true


@Given('user "{username}" has logged out from the client-UI')
def step(context, username):
    AccountSetting.logout()
    if not AccountSetting.is_user_signed_out():
        raise LookupError(f'Failed to logout user {username}')


@When('user "{username}" logs in using the client-UI')
def step(context, username):
    AccountSetting.login()
    password = get_password_for_user(username)
    enter_password = EnterPassword()
    enter_password.relogin(username, password)

    # wait for files to sync
    wait_for_initial_sync_to_complete(get_resource_path('/', username))
    Toolbar.wait_toolbar_enabled()


@When('user "|any|" opens login dialog')
def step(context, _):
    AccountSetting.login()


@Then('user "{username}" should be connected to the server')
def step(context, username):
    AccountSetting.wait_until_account_is_connected()


@When('the user removes the connection for user "{username}"')
def step(context, username):
    username = substitute_inline_codes(username)
    AccountSetting.remove_connection_for_user(username)


@Then('connection wizard should be visible')
def step(context):
    test.compare(
        AccountConnectionWizard.is_new_connection_window_visible(),
        True,
        'Connection window is visible',
    )


@When('the user accepts the certificate')
def step(context):
    AccountConnectionWizard.accept_certificate()


@When('the user adds the server "|any|"')
def step(context, server):
    server_url = substitute_inline_codes(server)
    AccountConnectionWizard.add_server(server_url)


@When('the user selects manual sync folder option in advanced section')
def step(context):
    AccountConnectionWizard.select_manual_sync_folder_option()
    AccountConnectionWizard.next_step()


@Then('credentials wizard should be visible')
def step(context):
    test.compare(
        AccountConnectionWizard.is_credential_window_visible(),
        True,
        'Credentials wizard is visible',
    )


@When('the user selects download everything option in advanced section')
def step(context):
    AccountConnectionWizard.select_download_everything_option()
    AccountConnectionWizard.next_step()


@When('the user opens the advanced configuration')
def step(context):
    AccountConnectionWizard.select_advanced_config()


@Then('the user should be able to choose the local download directory')
def step(context):
    test.compare(True, AccountConnectionWizard.can_change_local_sync_dir())


@Then('the download everything option should be selected by default for Linux')
def step(context):
    if is_linux():
        test.compare(
            True,
            AccountConnectionWizard.is_sync_everything_option_checked(),
            'Sync everything option is checked',
        )


@When(r'^the user presses the "([^"]*)" key(?:s)?', regexp=True)
def step(context, key):
    AccountSetting.press_key(key)


@Then('the log dialog should be opened')
def step(context):
    test.compare(True, AccountSetting.is_log_dialog_visible(), 'Log dialog is opened')


@When('the user cancels the sync connection wizard')
def step(context):
    SyncConnectionWizard.cancel_folder_sync_connection_wizard()


@When('the user quits the client')
def step(context):
    Toolbar.quit_qsfera()
    close_and_kill_app()


@Then('"{username}" account should be opened')
def step(context, username):
    username = substitute_inline_codes(username)
    expect(Toolbar.account_has_focus(username)).to.be.true


@Then(
    r'the default local sync path should contain "([^"]*)" in the (configuration|sync connection) wizard',
    regexp=True,
)
def step(context, sync_path, wizard):
    sync_path = substitute_inline_codes(sync_path)

    actual_sync_path = ''

    if wizard == 'configuration':
        actual_sync_path = AccountConnectionWizard.get_local_sync_path()
    else:
        actual_sync_path = SyncConnectionWizard.get_local_sync_path()

    test.compare(
        actual_sync_path,
        convert_path_separators_for_os(sync_path),
        'Compare sync path contains the expected path',
    )


@Then('the warning "|any|" should appear in the sync connection wizard')
def step(context, warn_message):
    actual_message = SyncConnectionWizard.get_warn_label()
    test.compare(
        True,
        warn_message in actual_message,
        'Contains warning message',
    )


@Given('the user has removed the connection for user "{username}"')
def step(context, username):
    username = substitute_inline_codes(username)
    AccountSetting.remove_connection_for_user(username)
    AccountSetting.wait_until_account_is_removed(username)
    shutil.rmtree(os.path.join(get_config("clientRootSyncPath"), username))
