from types import SimpleNamespace
from appium.webdriver.common.appiumby import AppiumBy as By
from selenium.webdriver.common.keys import Keys

from helpers.SetupClientHelper import get_current_user_sync_path
from helpers.AppHelper import app


class SyncConnectionWizard:
    CHOOSE_LOCAL_SYNC_FOLDER = SimpleNamespace(
        by=By.ACCESSIBILITY_ID, selector="localFolderLineEdit"
    )
    BACK_BUTTON = SimpleNamespace(by=By.NAME, selector="< Back")
    NEXT_BUTTON = SimpleNamespace(by=By.NAME, selector="Next >")
    SELECTIVE_SYNC_ROOT_FOLDER = SimpleNamespace(by=None, selector=None)
    ADD_SYNC_CONNECTION_BUTTON = SimpleNamespace(
        by=By.XPATH, selector="//dialog[@name='Add Space']//*[@name='Add Space']"
    )
    REMOTE_FOLDER_TREE = SimpleNamespace(by=None, selector=None)
    SELECTIVE_SYNC_TREE_HEADER = SimpleNamespace(by=None, selector=None)
    CANCEL_FOLDER_SYNC_CONNECTION_WIZARD = SimpleNamespace(
        by=By.NAME, selector="Cancel"
    )
    SPACES_LIST = SimpleNamespace(by=By.NAME, selector="Spaces list")
    SPACE_NAME_SELECTOR = SimpleNamespace(by=By.NAME, selector="{space_name},")
    CREATE_REMOTE_FOLDER_BUTTON = SimpleNamespace(by=None, selector=None)
    CREATE_REMOTE_FOLDER_INPUT = SimpleNamespace(by=None, selector=None)
    CREATE_REMOTE_FOLDER_CONFIRM_BUTTON = SimpleNamespace(by=None, selector=None)
    REFRESH_BUTTON = SimpleNamespace(by=None, selector=None)
    REMOTE_FOLDER_SELECTION_INPUT = SimpleNamespace(by=None, selector=None)
    ADD_FOLDER_SYNC_BUTTON = SimpleNamespace(by=None, selector=None)
    WARN_LABEL = SimpleNamespace(by=None, selector=None)
    CHOOSE_WHAT_TO_SYNC_FOLDER_TREE = SimpleNamespace(by=None, selector=None)

    @staticmethod
    def set_sync_path_oc(sync_path):
        if not sync_path:
            sync_path = get_current_user_sync_path()
        sync_path_input = app().find_element(
            SyncConnectionWizard.CHOOSE_LOCAL_SYNC_FOLDER.by,
            SyncConnectionWizard.CHOOSE_LOCAL_SYNC_FOLDER.selector,
        )
        sync_path_input.clear()
        sync_path_input.send_keys(sync_path)
        SyncConnectionWizard.next_step()

    @staticmethod
    def set_sync_path(sync_path=""):
        SyncConnectionWizard.set_sync_path_oc(sync_path)

    @staticmethod
    def next_step():
        next_button = app().find_element(
            SyncConnectionWizard.NEXT_BUTTON.by,
            SyncConnectionWizard.NEXT_BUTTON.selector,
        )
        if not next_button.is_enabled():
            raise AssertionError("Next button is not enabled")
        next_button.click()

    @staticmethod
    def back():
        squish.clickButton(squish.waitForObject(SyncConnectionWizard.BACK_BUTTON))

    @staticmethod
    def select_remote_destination_folder(folder):
        squish.mouseClick(
            squish.waitForObjectItem(SyncConnectionWizard.REMOTE_FOLDER_TREE, folder)
        )
        SyncConnectionWizard.next_step()

    @staticmethod
    def deselect_all_remote_folders():
        element = app().find_element(
            SyncConnectionWizard.ADD_SYNC_CONNECTION_BUTTON.by,
            SyncConnectionWizard.ADD_SYNC_CONNECTION_BUTTON.selector,
        )
        element.send_keys(Keys.ARROW_DOWN)
        element.native_send_keys(Keys.SPACE)  # uncheck the root folder

    @staticmethod
    def sort_by(header_text):
        squish.mouseClick(
            squish.waitForObject(
                {
                    "container": SyncConnectionWizard.SELECTIVE_SYNC_TREE_HEADER,
                    "text": header_text,
                    "type": "HeaderViewItem",
                    "visible": True,
                }
            )
        )

    @staticmethod
    def add_sync_connection():
        app().find_element(
            SyncConnectionWizard.ADD_SYNC_CONNECTION_BUTTON.by,
            SyncConnectionWizard.ADD_SYNC_CONNECTION_BUTTON.selector,
        ).click()

    @staticmethod
    def get_item_name_from_row(row_index):
        folder_row = {
            "row": row_index,
            "container": SyncConnectionWizard.SELECTIVE_SYNC_ROOT_FOLDER,
            "type": "QModelIndex",
        }
        return str(squish.waitForObjectExists(folder_row).displayText)

    @staticmethod
    def is_root_folder_checked():
        state = squish.waitForObject(SyncConnectionWizard.SELECTIVE_SYNC_ROOT_FOLDER)[
            "checkState"
        ]
        return state == "checked"

    @staticmethod
    def cancel_folder_sync_connection_wizard():
        app().find_element(
            SyncConnectionWizard.CANCEL_FOLDER_SYNC_CONNECTION_WIZARD.by,
            SyncConnectionWizard.CANCEL_FOLDER_SYNC_CONNECTION_WIZARD.selector,
        ).click()

    @staticmethod
    def select_space(space_name):
        spaces_list = app().find_element(
            SyncConnectionWizard.SPACES_LIST.by,
            SyncConnectionWizard.SPACES_LIST.selector,
        )
        space_item = spaces_list.find_element(
            SyncConnectionWizard.SPACE_NAME_SELECTOR.by,
            SyncConnectionWizard.SPACE_NAME_SELECTOR.selector.format(
                space_name=space_name
            ),
        )
        # ISSUE: https://github.com/opencloud-eu/desktop/pull/879
        # Cannot select space by click event
        # Select space using keyboard events as a workaround
        # TODO: Remove 'send_keys' and uncomment 'click' action
        space_item.send_keys(Keys.ARROW_DOWN)
        # space_item.click()
        if space_item.get_attribute("selected") != "true":
            raise AssertionError("Failed to select the space: " + space_name)

    @staticmethod
    def sync_space(space_name):
        SyncConnectionWizard.set_sync_path(get_current_user_sync_path())
        SyncConnectionWizard.select_space(space_name)
        SyncConnectionWizard.next_step()
        SyncConnectionWizard.add_sync_connection()

    @staticmethod
    def create_folder_in_remote_destination(folder_name):
        squish.clickButton(
            squish.waitForObject(SyncConnectionWizard.CREATE_REMOTE_FOLDER_BUTTON)
        )
        squish.type(
            squish.waitForObject(SyncConnectionWizard.CREATE_REMOTE_FOLDER_INPUT),
            folder_name,
        )
        squish.clickButton(
            squish.waitForObject(
                SyncConnectionWizard.CREATE_REMOTE_FOLDER_CONFIRM_BUTTON
            )
        )

    @staticmethod
    def refresh_remote():
        squish.clickButton(squish.waitForObject(SyncConnectionWizard.REFRESH_BUTTON))

    @staticmethod
    def is_remote_folder_selected(folder_selector):
        return squish.waitForObjectExists(folder_selector).selected

    @staticmethod
    def open_sync_connection_wizard():
        squish.mouseClick(
            squish.waitForObject(SyncConnectionWizard.ADD_FOLDER_SYNC_BUTTON)
        )

    @staticmethod
    def get_local_sync_path():
        return str(
            squish.waitForObjectExists(
                SyncConnectionWizard.CHOOSE_LOCAL_SYNC_FOLDER
            ).displayText
        )

    @staticmethod
    def get_warn_label():
        return str(squish.waitForObjectExists(SyncConnectionWizard.WARN_LABEL).text)

    @staticmethod
    def is_add_sync_folder_button_enabled():
        return squish.waitForObjectExists(
            SyncConnectionWizard.ADD_FOLDER_SYNC_BUTTON
        ).enabled

    @staticmethod
    def select_or_unselect_folders_to_sync(folders, select=True):
        expected_state = "true" if select else "false"

        if select:
            SyncConnectionWizard.deselect_all_remote_folders()

        for folder_path in folders:
            parents = folder_path.strip("/").split("/")
            target_folder = parents.pop()

            parent_element = None
            parent_position = 0
            for parent in parents:
                p_elements = app().find_elements(By.NAME, parent)
                # select nested folders based on the position of the parent folder
                for p_element in p_elements:
                    if (
                        p_element.get_attribute("checked") == 'true'
                        and p_element.rect["x"] > parent_position
                    ):
                        parent_element = p_element
                        parent_position = p_element.rect["x"]
                        break
                parent_element.native_double_click()  # expand the folder
                # retry once if the folder is not expanded
                if parent_element.is_selected():
                    print('[WARN] Folder was not expanded, retrying with space key')
                    # expand using space key
                    parent_element.native_click()
                    parent_element.native_send_keys(Keys.SPACE)
                if parent_element.is_selected():
                    raise AssertionError(f'Failed to expand folder: {parent}')

            folder_element = None
            target_folders = app().find_elements(By.NAME, target_folder)
            # select the folder that is inside the current parent position
            for folder in target_folders:
                if folder.rect["x"] > parent_position:
                    folder_element = folder
                    break
            is_checked = folder_element.get_attribute("checked")
            # return early if the folder is already in the expected state.
            if is_checked == expected_state:
                return

            folder_element.native_click()
            if not folder_element.is_selected():
                raise AssertionError(f"Failed to focus folder: {target_folder}")
            folder_element.native_send_keys(Keys.SPACE)  # toggle the folder selection

            is_checked = folder_element.get_attribute("checked")
            if is_checked != expected_state:
                raise AssertionError(
                    f"Failed to {'select' if select else 'unselect'} folder: {folder_path}"
                )

    @staticmethod
    def confirm_choose_what_to_sync_selection():
        app().find_element(By.NAME, "OK").click()

    @staticmethod
    def __handle_folder_selection(folders, should_select, new_sync_connection_wizard):
        SyncConnectionWizard.select_or_unselect_folders_to_sync(folders, should_select)

        if new_sync_connection_wizard:
            SyncConnectionWizard.add_sync_connection()
        else:
            SyncConnectionWizard.confirm_choose_what_to_sync_selection()

    @staticmethod
    def unselect_folders_to_sync(folders, new_sync_connection_wizard=False):
        SyncConnectionWizard.__handle_folder_selection(
            folders,
            should_select=False,
            new_sync_connection_wizard=new_sync_connection_wizard,
        )

    @staticmethod
    def select_folders_to_sync(folders, new_sync_connection_wizard=False):
        SyncConnectionWizard.__handle_folder_selection(
            folders,
            should_select=True,
            new_sync_connection_wizard=new_sync_connection_wizard,
        )
