# -*- coding: utf-8 -*-
"""
Teams pages.
"""

from common.test.acceptance.pages.lms.course_page import CoursePage
from common.test.acceptance.pages.lms.discussion import InlineDiscussionPage
from common.test.acceptance.pages.common.paging import PaginatedUIMixin
from common.test.acceptance.pages.common.utils import confirm_prompt

from common.test.acceptance.pages.lms.fields import FieldsMixin


TOPIC_CARD_CSS = 'div.wrapper-card-core'
CARD_TITLE_CSS = 'h3.card-title'
MY_TEAMS_BUTTON_CSS = '.nav-item[data-index="0"]'
BROWSE_BUTTON_CSS = '.nav-item[data-index="1"]'
TEAMS_LINK_CSS = '.action-view'
TEAMS_HEADER_CSS = '.teams-header'
CREATE_TEAM_LINK_CSS = '.create-team'


class TeamCardsMixin(object):
    """Provides common operations on the team card component."""

    def _bounded_selector(self, css):
        """Bind the CSS to a particular tabpanel (e.g. My Teams or Browse)."""
        return '{tabpanel_id} {css}'.format(tabpanel_id=getattr(self, 'tabpanel_id', ''), css=css)

    def view_first_team(self):
        """Click the 'view' button of the first team card on the page."""
        self.q(css=self._bounded_selector('a.action-view')).first.click()

    @property
    def team_cards(self):
        """Get all the team cards on the page."""
        return self.q(css=self._bounded_selector('.team-card'))

    @property
    def team_names(self):
        """Return the names of each team on the page."""
        return self.q(css=self._bounded_selector('h3.card-title')).map(lambda e: e.text).results

    @property
    def team_descriptions(self):
        """Return the names of each team on the page."""
        return self.q(css=self._bounded_selector('p.card-description')).map(lambda e: e.text).results

    @property
    def team_memberships(self):
        """Return the team memberships text for each card on the page."""
        return self.q(css=self._bounded_selector('.member-count')).map(lambda e: e.text).results


class BreadcrumbsMixin(object):
    """Provides common operations on teams page breadcrumb links."""

    @property
    def header_page_breadcrumbs(self):
        """Get the page breadcrumb text displayed by the page header"""
        return self.q(css='.page-header .breadcrumbs')[0].text

    def click_all_topics(self):
        """ Click on the "All Topics" breadcrumb """
        self.q(css='a.nav-item').filter(text='All Topics')[0].click()

    def click_specific_topic(self, topic):
        """ Click on the breadcrumb for a specific topic """
        self.q(css='a.nav-item').filter(text=topic)[0].click()


class TeamsPage(CoursePage, BreadcrumbsMixin):
    """
    Teams page/tab.
    """
    url_path = "teams"

    def is_browser_on_page(self):
        """ Checks if teams page is being viewed """
        return self.q(css='body.view-teams').present

    def get_body_text(self):
        """ Returns the current dummy text. This will be changed once there is more content on the page. """
        main_page_content_css = '.page-content-main'
        self.wait_for(
            lambda: len(self.q(css=main_page_content_css).text) == 1,
            description="Body text is present"
        )
        return self.q(css=main_page_content_css).text[0]

    def active_tab(self):
        """ Get the active tab. """
        return self.q(css='.is-active').attrs('data-url')[0]

    def browse_topics(self):
        """ View the Browse tab of the Teams page. """
        self.q(css=BROWSE_BUTTON_CSS).click()

    def verify_team_count_in_first_topic(self, expected_count):
        """
        Verify that the team count on the first topic card in the topic list is correct
        (browse topics page).
        """
        self.wait_for(
            lambda: self.q(css='.team-count')[0].text == "0 Teams" if expected_count == 0 else "1 Team",
            description="Team count text on topic is wrong"
        )

    def verify_topic_team_count(self, expected_count):
        """ Verify the number of teams listed on the topic page (browse teams within topic). """
        self.wait_for(
            lambda: len(self.q(css='.team-card')) == expected_count,
            description="Expected number of teams is wrong"
        )

    def verify_my_team_count(self, expected_count):
        """ Verify the number of teams on 'My Team'. """

        # Click to "My Team" and verify that it contains the expected number of teams.
        self.q(css=MY_TEAMS_BUTTON_CSS).click()
        self.wait_for_ajax()
        self.wait_for(
            lambda: len(self.q(css='.team-card')) == expected_count,
            description="Expected number of teams is wrong"
        )

    def click_all_topics(self):
        """ Click on the "All Topics" breadcrumb """
        self.q(css='a.nav-item').filter(text='All Topics')[0].click()

    def click_specific_topic(self, topic):
        """ Click on the breadcrumb for a specific topic """
        self.q(css='a.nav-item').filter(text=topic)[0].click()

    @property
    def warning_message(self):
        """Return the text of the team warning message."""
        return self.q(css='.warning').results[0].text


class MyTeamsPage(CoursePage, PaginatedUIMixin, TeamCardsMixin):
    """
    The 'My Teams' tab of the Teams page.
    """

    url_path = "teams/#my-teams"
    tabpanel_id = '#tabpanel-my-teams'

    def is_browser_on_page(self):
        """Check if the "My Teams" tab is being viewed."""
        button_classes = self.q(css=MY_TEAMS_BUTTON_CSS).attrs('class')
        if len(button_classes) == 0:
            return False
        return 'is-active' in button_classes[0]


class BrowseTopicsPage(CoursePage, PaginatedUIMixin):
    """
    The 'Browse' tab of the Teams page.
    """

    url_path = "teams/#browse"

    def is_browser_on_page(self):
        """Check if the Browse tab is being viewed."""
        button_classes = self.q(css=BROWSE_BUTTON_CSS).attrs('class')
        if len(button_classes) == 0:
            return False
        return 'is-active' in button_classes[0]

    @property
    def topic_cards(self):
        """Return a list of the topic cards present on the page."""
        return self.q(css=TOPIC_CARD_CSS).results

    @property
    def topic_names(self):
        """Return a list of the topic names present on the page."""
        return self.q(css='#tabpanel-browse ' + CARD_TITLE_CSS).map(lambda e: e.text).results

    @property
    def topic_descriptions(self):
        """Return a list of the topic descriptions present on the page."""
        return self.q(css='p.card-description').map(lambda e: e.text).results

    def browse_teams_for_topic(self, topic_name):
        """
        Show the teams list for `topic_name`.
        """
        self.q(css=TEAMS_LINK_CSS).filter(
            text='View Teams in the {topic_name} Topic'.format(topic_name=topic_name)
        )[0].click()
        self.wait_for_ajax()

    def sort_topics_by(self, sort_order):
        """Sort the list of topics by the given `sort_order`."""
        self.q(
            css='#paging-header-select option[value={sort_order}]'.format(sort_order=sort_order)
        ).click()
        self.wait_for_ajax()


class BaseTeamsPage(CoursePage, PaginatedUIMixin, TeamCardsMixin, BreadcrumbsMixin):
    """
    The paginated UI for browsing teams within a Topic on the Teams
    page.
    """
    def __init__(self, browser, course_id, topic):
        """
        Note that `topic` is a dict representation of a topic following
        the same convention as a course module's topic.
        """
        super(BaseTeamsPage, self).__init__(browser, course_id)
        self.topic = topic

    def is_browser_on_page(self):
        """Check if we're on a teams list page for a particular topic."""
        has_correct_url = self.url.endswith(self.url_path)
        teams_list_view_present = self.q(css='.teams-main').present
        return has_correct_url and teams_list_view_present

    @property
    def header_name(self):
        """Get the topic name displayed by the page header"""
        return self.q(css=TEAMS_HEADER_CSS + ' .page-title')[0].text

    @property
    def header_description(self):
        """Get the topic description displayed by the page header"""
        return self.q(css=TEAMS_HEADER_CSS + ' .page-description')[0].text

    @property
    def sort_order(self):
        """Return the current sort order on the page."""
        return self.q(
            css='#paging-header-select option'
        ).filter(
            lambda e: e.is_selected()
        ).results[0].text.strip()

    @property
    def team_names(self):
        """Get all the team names on the page."""
        return self.q(css=CARD_TITLE_CSS).map(lambda e: e.text).results

    def click_create_team_link(self):
        """ Click on create team link."""
        query = self.q(css=CREATE_TEAM_LINK_CSS)
        if query.present:
            query.first.click()
            self.wait_for_ajax()

    def click_search_team_link(self):
        """ Click on create team link."""
        query = self.q(css='.search-team-descriptions')
        if query.present:
            query.first.click()
            self.wait_for_ajax()

    def click_browse_all_teams_link(self):
        """ Click on browse team link."""
        query = self.q(css='.browse-teams')
        if query.present:
            query.first.click()
            self.wait_for_ajax()

    def sort_teams_by(self, sort_order):
        """Sort the list of teams by the given `sort_order`."""
        self.q(
            css='#paging-header-select option[value={sort_order}]'.format(sort_order=sort_order)
        ).click()
        self.wait_for_ajax()

    @property
    def _showing_search_results(self):
        """
        Returns true if showing search results.
        """
        return self.header_description.startswith(u"Showing results for")

    def search(self, string):
        """
        Searches for the specified string, and returns a SearchTeamsPage
        representing the search results page.
        """
        self.q(css='.search-field').first.fill(string)
        self.q(css='.action-search').first.click()
        self.wait_for_ajax()
        self.wait_for(
            lambda: self._showing_search_results,
            description="Showing search results"
        )
        page = SearchTeamsPage(self.browser, self.course_id, self.topic)
        page.wait_for_page()
        return page


class BrowseTeamsPage(BaseTeamsPage):
    """
    The paginated UI for browsing teams within a Topic on the Teams
    page.
    """
    def __init__(self, browser, course_id, topic):
        super(BrowseTeamsPage, self).__init__(browser, course_id, topic)
        self.url_path = "teams/#topics/{topic_id}".format(topic_id=self.topic['id'])


class SearchTeamsPage(BaseTeamsPage):
    """
    The paginated UI for showing team search results.
    page.
    """
    def __init__(self, browser, course_id, topic):
        super(SearchTeamsPage, self).__init__(browser, course_id, topic)
        self.url_path = "teams/#topics/{topic_id}/search".format(topic_id=self.topic['id'])


class TeamManagementPage(CoursePage, FieldsMixin, BreadcrumbsMixin):
    """
    Team page for creation, editing, and deletion.
    """
    def __init__(self, browser, course_id, topic):
        """
        Set up `self.url_path` on instantiation, since it dynamically
        reflects the current topic.  Note that `topic` is a dict
        representation of a topic following the same convention as a
        course module's topic.
        """
        super(TeamManagementPage, self).__init__(browser, course_id)
        self.topic = topic
        self.url_path = "teams/#topics/{topic_id}/create-team".format(topic_id=self.topic['id'])

    def is_browser_on_page(self):
        """Check if we're on the create team page for a particular topic."""
        return self.q(css='.team-edit-fields').present

    @property
    def header_page_name(self):
        """Get the page name displayed by the page header"""
        return self.q(css='.page-header .page-title')[0].text

    @property
    def header_page_description(self):
        """Get the page description displayed by the page header"""
        return self.q(css='.page-header .page-description')[0].text

    @property
    def validation_message_text(self):
        """Get the error message text"""
        return self.q(css='.create-team.wrapper-msg .copy')[0].text

    def submit_form(self):
        """Click on create team button"""
        self.q(css='.create-team .action-primary').first.click()
        self.wait_for_ajax()

    def cancel_team(self):
        """Click on cancel team button"""
        self.q(css='.create-team .action-cancel').first.click()
        self.wait_for_ajax()

    @property
    def delete_team_button(self):
        """Returns the 'delete team' button."""
        return self.q(css='.action-delete').first

    def click_membership_button(self):
        """Clicks the 'edit membership' button"""
        self.q(css='.action-edit-members').first.click()
        self.wait_for_ajax()

    @property
    def membership_button_present(self):
        """Checks if the edit membership button is present"""
        return self.q(css='.action-edit-members').present


class EditMembershipPage(CoursePage):
    """
    Staff or discussion-privileged user page to remove troublesome or inactive
    students from a team
    """
    def __init__(self, browser, course_id, team):
        """
        Set up `self.url_path` on instantiation, since it dynamically
        reflects the current team.
        """
        super(EditMembershipPage, self).__init__(browser, course_id)
        self.team = team
        self.url_path = "teams/#teams/{topic_id}/{team_id}/edit-team/manage-members".format(
            topic_id=self.team['topic_id'], team_id=self.team['id']
        )

    def is_browser_on_page(self):
        """Check if we're on the team membership page for a particular team."""
        self.wait_for_ajax()

        if self.q(css='.edit-members').present:
            return True
        empty_query = self.q(css='.teams-main>.page-content>p').first
        return (
            len(empty_query.results) > 0 and
            empty_query[0].text == "This team does not have any members."
        )

    @property
    def team_members(self):
        """Returns the number of team members shown on the page."""
        return len(self.q(css='.team-member'))

    def click_first_remove(self):
        """Clicks the remove link on the first member listed."""
        self.q(css='.action-remove-member').first.click()

    def confirm_delete_membership_dialog(self):
        """Click 'delete' on the warning dialog."""
        confirm_prompt(self, require_notification=False)
        self.wait_for_ajax()

    def cancel_delete_membership_dialog(self):
        """Click 'delete' on the warning dialog."""
        confirm_prompt(self, cancel=True)


class TeamPage(CoursePage, PaginatedUIMixin, BreadcrumbsMixin):
    """
    The page for a specific Team within the Teams tab
    """
    def __init__(self, browser, course_id, team=None):
        """
        Set up `self.url_path` on instantiation, since it dynamically
        reflects the current team.
        """
        super(TeamPage, self).__init__(browser, course_id)
        self.team = team
        if self.team:
            self.url_path = "teams/#teams/{topic_id}/{team_id}".format(
                topic_id=self.team['topic_id'], team_id=self.team['id']
            )

    def is_browser_on_page(self):
        """Check if we're on the teams list page for a particular team."""
        self.wait_for_ajax()
        if self.team:
            if not self.url.endswith(self.url_path):
                return False
        return self.q(css='.team-profile').present

    @property
    def discussion_id(self):
        """Get the id of the discussion module on the page"""
        return self.q(css='div.discussion-module').attrs('data-discussion-id')[0]

    @property
    def discussion_page(self):
        """Get the discussion as a bok_choy page object"""
        if not hasattr(self, '_discussion_page'):
            # pylint: disable=attribute-defined-outside-init
            self._discussion_page = InlineDiscussionPage(self.browser, self.discussion_id)
        return self._discussion_page

    @property
    def team_name(self):
        """Get the team's name as displayed in the page header"""
        return self.q(css='.page-header .page-title')[0].text

    @property
    def team_description(self):
        """Get the team's description as displayed in the page header"""
        return self.q(css=TEAMS_HEADER_CSS + ' .page-description')[0].text

    @property
    def team_members_present(self):
        """Verifies that team members are present"""
        return self.q(css='.page-content-secondary .team-members .team-member').present

    @property
    def team_capacity_text(self):
        """Returns team capacity text"""
        return self.q(css='.page-content-secondary .team-capacity :last-child').text[0]

    @property
    def team_location(self):
        """ Returns team location/country. """
        return self.q(css='.page-content-secondary .team-country :last-child').text[0]

    @property
    def team_language(self):
        """ Returns team location/country. """
        return self.q(css='.page-content-secondary .team-language :last-child').text[0]

    @property
    def team_user_membership_text(self):
        """Returns the team membership text"""
        query = self.q(css='.page-content-secondary > .team-user-membership-status')
        return query.text[0] if query.present else ''

    @property
    def team_leave_link_present(self):
        """Verifies that team leave link is present"""
        return self.q(css='.leave-team-link').present

    def click_leave_team_link(self, remaining_members=0, cancel=False):
        """ Click on Leave Team link"""
        self.q(css='.leave-team-link').first.click()
        confirm_prompt(self, cancel, require_notification=False)

        if cancel is False:
            self.wait_for(
                lambda: self.join_team_button_present,
                description="Join Team button did not become present"
            )
            self.wait_for_capacity_text(remaining_members)

    @property
    def team_members(self):
        """Returns the number of team members in this team"""
        return len(self.q(css='.page-content-secondary .team-member'))

    def click_first_profile_image(self):
        """Clicks on first team member's profile image"""
        self.q(css='.page-content-secondary .members-info .team-member').first.click()

    @property
    def first_member_username(self):
        """Returns the username of team member"""
        return self.q(css='.page-content-secondary .tooltip-custom').text[0]

    def click_join_team_button(self, total_members=1):
        """ Click on Join Team button"""
        self.q(css='.join-team .action-primary').first.click()
        self.wait_for(
            lambda: not self.join_team_button_present,
            description="Join Team button did not go away"
        )
        self.wait_for_capacity_text(total_members)

    def wait_for_capacity_text(self, num_members, max_size=10):
        """ Wait for the team capacity text to be correct. """
        self.wait_for(
            lambda: self.team_capacity_text == self.format_capacity_text(num_members, max_size),
            description="Team capacity text is not correct"
        )

    def format_capacity_text(self, num_members, max_size):
        """ Helper method to format the expected team capacity text. """
        return '{num_members} / {max_size} {members_text}'.format(
            num_members=num_members,
            max_size=max_size,
            members_text='Member' if num_members == max_size else 'Members'
        )

    @property
    def join_team_message(self):
        """ Returns join team message """
        self.wait_for_ajax()
        return self.q(css='.join-team .join-team-message').text[0]

    @property
    def join_team_button_present(self):
        """ Returns True if Join Team button is present else False """
        return self.q(css='.join-team .action-primary').present

    @property
    def join_team_message_present(self):
        """ Returns True if Join Team message is present else False """
        return self.q(css='.join-team .join-team-message').present

    @property
    def new_post_button_present(self):
        """ Returns True if New Post button is present else False """
        return self.q(css='.discussion-module .new-post-btn').present

    @property
    def edit_team_button_present(self):
        """ Returns True if Edit Team button is present else False """
        return self.q(css='.form-actions .action-edit-team').present

    def click_edit_team_button(self):
        """ Click on Edit Team button"""
        self.q(css='.form-actions .action-edit-team').first.click()
