from bok_choy.page_object import PageObject
from selenium.webdriver.common.keys import Keys
from common.test.acceptance.pages.common.utils import click_css
from common.test.acceptance.tests.helpers import select_option_by_text, get_selected_option_text
from selenium.webdriver.support.ui import Select


class BaseComponentEditorView(PageObject):
    """
    A base :class:`.PageObject` for the component and visibility editors.

    This class assumes that the editor is our default editor as displayed for xmodules.
    """
    BODY_SELECTOR = '.xblock-editor'

    def __init__(self, browser, locator):
        """
        Args:
            browser (selenium.webdriver): The Selenium-controlled browser that this page is loaded in.
            locator (str): The locator that identifies which xblock this :class:`.xblock-editor` relates to.
        """
        super(BaseComponentEditorView, self).__init__(browser)
        self.locator = locator

    def is_browser_on_page(self):
        return self.q(css='{}[data-locator="{}"]'.format(self.BODY_SELECTOR, self.locator)).present

    def _bounded_selector(self, selector):
        """
        Return `selector`, but limited to this particular `ComponentEditorView` context
        """
        return '{}[data-locator="{}"] {}'.format(
            self.BODY_SELECTOR,
            self.locator,
            selector
        )

    def url(self):
        """
        Returns None because this is not directly accessible via URL.
        """
        return None

    def save(self):
        """
        Clicks save button.
        """
        click_css(self, 'a.action-save')

    def cancel(self):
        """
        Clicks cancel button.
        """
        click_css(self, 'a.action-cancel', require_notification=False)


class ComponentEditorView(BaseComponentEditorView):
    """
    A :class:`.PageObject` representing the rendered view of a component editor.
    """
    def get_setting_element(self, label):
        """
        Returns the index of the setting entry with given label (display name) within the Settings modal.
        """
        settings_button = self.q(css='.edit-xblock-modal .editor-modes .settings-button')
        if settings_button.is_present():
            settings_button.click()
        setting_labels = self.q(css=self._bounded_selector('.metadata_edit .wrapper-comp-setting .setting-label'))
        for index, setting in enumerate(setting_labels):
            if setting.text == label:
                return self.q(css=self._bounded_selector('.metadata_edit div.wrapper-comp-setting .setting-input'))[index]
        return None

    def set_field_value_and_save(self, label, value):
        """
        Sets the text field with given label (display name) to the specified value, and presses Save.
        """
        elem = self.get_setting_element(label)

        # Clear the current value, set the new one, then
        # Tab to move to the next field (so change event is triggered).
        elem.clear()
        elem.send_keys(value)
        elem.send_keys(Keys.TAB)
        self.save()

    def set_select_value_and_save(self, label, value):
        """
        Sets the select with given label (display name) to the specified value, and presses Save.
        """
        elem = self.get_setting_element(label)
        select = Select(elem)
        select.select_by_value(value)
        self.save()

    def get_selected_option_text(self, label):
        """
        Returns the text of the first selected option for the select with given label (display name).
        """
        elem = self.get_setting_element(label)
        if elem:
            select = Select(elem)
            return select.first_selected_option.text
        else:
            return None


class ComponentVisibilityEditorView(BaseComponentEditorView):
    """
    A :class:`.PageObject` representing the rendered view of a component visibility editor.
    """
    OPTION_SELECTOR = '.partition-group-control .field'
    ALL_LEARNERS_AND_STAFF = 'All Learners and Staff'
    CONTENT_GROUP_PARTITION = 'Content Groups'
    ENROLLMENT_TRACK_PARTITION = "Enrollment Tracks"

    @property
    def all_group_options(self):
        """
        Return all partition groups.
        """
        return self.q(css=self._bounded_selector(self.OPTION_SELECTOR)).results

    @property
    def current_groups_message(self):
        """
        This returns the message shown at the top of the visibility dialog about the
        current visibility state (at the time that the dialog was opened).
        For example, "Current visible to: All Learners and Staff".
        """
        return self.q(css=self._bounded_selector('.visibility-header'))[0].text

    @property
    def selected_partition_scheme(self):
        """
        Return the selected partition scheme (or "All Learners and Staff"
        if no partitioning is selected).
        """
        selector = self.q(css=self._bounded_selector('.partition-visibility select'))
        return get_selected_option_text(selector)

    def select_partition_scheme(self, partition_name):
        """
        Sets the selected partition scheme to the one with the
        matching name.
        """
        selector = self.q(css=self._bounded_selector('.partition-visibility select'))
        select_option_by_text(selector, partition_name, focus_out=True)

    @property
    def selected_groups(self):
        """
        Return all selected partition groups. If none are selected,
        returns an empty array.
        """
        results = []
        for option in self.all_group_options:
            checkbox = option.find_element_by_css_selector('input')
            if checkbox.is_selected():
                results.append(option)
        return results

    def select_group(self, group_name, save=True):
        """
        Select the first group which has a label matching `group_name`.

        Arguments:
            group_name (str): The name of the group.
            save (boolean): Whether the "save" button should be clicked
                afterwards.
        Returns:
            bool: Whether a group with the provided name was found and clicked.
        """
        for option in self.all_group_options:
            if group_name in option.text:
                checkbox = option.find_element_by_css_selector('input')
                checkbox.click()
                if save:
                    self.save()
                return True
        return False

    def select_groups_in_partition_scheme(self, partition_name, group_names):
        """
        Select groups in the provided partition scheme. The "save"
        button is clicked afterwards.
        """
        self.select_partition_scheme(partition_name)
        for label in group_names:
            self.select_group(label, save=False)
        self.save()
