# -*- coding: utf-8 -*-
"""
End-to-end tests for LibraryContent block in LMS
"""
import ddt
import textwrap

from nose.plugins.attrib import attr
from ..helpers import UniqueCourseTest
from ...pages.studio.auto_auth import AutoAuthPage
from ...pages.studio.overview import CourseOutlinePage
from ...pages.studio.library import StudioLibraryContentEditor, StudioLibraryContainerXBlockWrapper
from ...pages.lms.courseware import CoursewarePage
from ...pages.lms.library import LibraryContentXBlockWrapper
from ...pages.common.logout import LogoutPage
from ...fixtures.course import CourseFixture, XBlockFixtureDesc
from ...fixtures.library import LibraryFixture

SECTION_NAME = 'Test Section'
SUBSECTION_NAME = 'Test Subsection'
UNIT_NAME = 'Test Unit'


@attr('shard_3')
class LibraryContentTestBase(UniqueCourseTest):
    """ Base class for library content block tests """
    USERNAME = "STUDENT_TESTER"
    EMAIL = "student101@example.com"

    STAFF_USERNAME = "STAFF_TESTER"
    STAFF_EMAIL = "staff101@example.com"

    def populate_library_fixture(self, library_fixture):
        """
        To be overwritten by subclassed tests. Used to install a library to
        run tests on.
        """

    def setUp(self):
        """
        Set up library, course and library content XBlock
        """
        super(LibraryContentTestBase, self).setUp()

        self.courseware_page = CoursewarePage(self.browser, self.course_id)

        self.course_outline = CourseOutlinePage(
            self.browser,
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run']
        )

        self.library_fixture = LibraryFixture('test_org', self.unique_id, 'Test Library {}'.format(self.unique_id))
        self.populate_library_fixture(self.library_fixture)

        self.library_fixture.install()
        self.library_info = self.library_fixture.library_info
        self.library_key = self.library_fixture.library_key

        # Install a course with library content xblock
        self.course_fixture = CourseFixture(
            self.course_info['org'], self.course_info['number'],
            self.course_info['run'], self.course_info['display_name']
        )

        library_content_metadata = {
            'source_library_id': unicode(self.library_key),
            'mode': 'random',
            'max_count': 1,
            'has_score': False
        }

        self.lib_block = XBlockFixtureDesc('library_content', "Library Content", metadata=library_content_metadata)

        self.course_fixture.add_children(
            XBlockFixtureDesc('chapter', SECTION_NAME).add_children(
                XBlockFixtureDesc('sequential', SUBSECTION_NAME).add_children(
                    XBlockFixtureDesc('vertical', UNIT_NAME).add_children(
                        self.lib_block
                    )
                )
            )
        )

        self.course_fixture.install()

    def _change_library_content_settings(self, count=1, capa_type=None):
        """
        Performs library block refresh in Studio, configuring it to show {count} children
        """
        unit_page = self._go_to_unit_page(True)
        library_container_block = StudioLibraryContainerXBlockWrapper.from_xblock_wrapper(unit_page.xblocks[1])
        library_container_block.edit()
        editor = StudioLibraryContentEditor(self.browser, library_container_block.locator)
        editor.count = count
        if capa_type is not None:
            editor.capa_type = capa_type
        editor.save()
        self._go_to_unit_page(change_login=False)
        unit_page.wait_for_page()
        unit_page.publish_action.click()
        unit_page.wait_for_ajax()
        self.assertIn("Published and Live", unit_page.publish_title)

    @property
    def library_xblocks_texts(self):
        """
        Gets texts of all xblocks in library
        """
        return frozenset(child.data for child in self.library_fixture.children)

    def _go_to_unit_page(self, change_login=True):
        """
        Open unit page in Studio
        """
        if change_login:
            LogoutPage(self.browser).visit()
            self._auto_auth(self.STAFF_USERNAME, self.STAFF_EMAIL, True)
        self.course_outline.visit()
        subsection = self.course_outline.section(SECTION_NAME).subsection(SUBSECTION_NAME)
        return subsection.toggle_expand().unit(UNIT_NAME).go_to()

    def _goto_library_block_page(self, block_id=None):
        """
        Open library page in LMS
        """
        self.courseware_page.visit()
        paragraphs = self.courseware_page.q(css='.course-content p')
        if paragraphs and "You were most recently in" in paragraphs.text[0]:
            paragraphs[0].find_element_by_tag_name('a').click()
        block_id = block_id if block_id is not None else self.lib_block.locator
        #pylint: disable=attribute-defined-outside-init
        self.library_content_page = LibraryContentXBlockWrapper(self.browser, block_id)
        self.library_content_page.wait_for_page()

    def _auto_auth(self, username, email, staff):
        """
        Logout and login with given credentials.
        """
        AutoAuthPage(self.browser, username=username, email=email,
                     course_id=self.course_id, staff=staff).visit()


@ddt.ddt
@attr('shard_3')
class LibraryContentTest(LibraryContentTestBase):
    """
    Test courseware.
    """
    def populate_library_fixture(self, library_fixture):
        """
        Populates library fixture with XBlock Fixtures
        """
        library_fixture.add_children(
            XBlockFixtureDesc("html", "Html1", data='html1'),
            XBlockFixtureDesc("html", "Html2", data='html2'),
            XBlockFixtureDesc("html", "Html3", data='html3'),
        )

    @ddt.data(1, 2, 3)
    def test_shows_random_xblocks_from_configured(self, count):
        """
        Scenario: Ensures that library content shows {count} random xblocks from library in LMS
        Given I have a library, a course and a LibraryContent block in that course
        When I go to studio unit page for library content xblock as staff
        And I set library content xblock to display {count} random children
        And I refresh library content xblock and pulbish unit
        When I go to LMS courseware page for library content xblock as student
        Then I can see {count} random xblocks from the library
        """
        self._change_library_content_settings(count=count)
        self._auto_auth(self.USERNAME, self.EMAIL, False)
        self._goto_library_block_page()
        children_contents = self.library_content_page.children_contents
        self.assertEqual(len(children_contents), count)
        self.assertLessEqual(children_contents, self.library_xblocks_texts)

    def test_shows_all_if_max_set_to_greater_value(self):
        """
        Scenario: Ensures that library content shows {count} random xblocks from library in LMS
        Given I have a library, a course and a LibraryContent block in that course
        When I go to studio unit page for library content xblock as staff
        And I set library content xblock to display more children than library have
        And I refresh library content xblock and pulbish unit
        When I go to LMS courseware page for library content xblock as student
        Then I can see all xblocks from the library
        """
        self._change_library_content_settings(count=10)
        self._auto_auth(self.USERNAME, self.EMAIL, False)
        self._goto_library_block_page()
        children_contents = self.library_content_page.children_contents
        self.assertEqual(len(children_contents), 3)
        self.assertEqual(children_contents, self.library_xblocks_texts)


@ddt.ddt
@attr('shard_3')
class StudioLibraryContainerCapaFilterTest(LibraryContentTestBase):
    """
    Test Library Content block in LMS
    """
    def _get_problem_choice_group_text(self, name, items):
        """ Generates Choice Group CAPA problem XML """
        items_text = "\n".join([
            "<choice correct='{correct}'>{item}</choice>".format(correct=correct, item=item)
            for item, correct in items
        ])

        return textwrap.dedent("""
        <problem>
            <p>{name}</p>
            <multiplechoiceresponse>
                <choicegroup label="{name}" type="MultipleChoice">{items}</choicegroup>
            </multiplechoiceresponse>
        </problem>""").format(name=name, items=items_text)

    def _get_problem_select_text(self, name, items, correct):
        """ Generates Select Option CAPA problem XML """
        items_text = ",".join(["'{0}'".format(item) for item in items])

        return textwrap.dedent("""
        <problem>
            <p>{name}</p>
            <optionresponse>
              <optioninput label="{name}" options="({options})" correct="{correct}"></optioninput>
            </optionresponse>
        </problem>""").format(name=name, options=items_text, correct=correct)

    def populate_library_fixture(self, library_fixture):
        """
        Populates library fixture with XBlock Fixtures
        """
        library_fixture.add_children(
            XBlockFixtureDesc(
                "problem", "Problem Choice Group 1",
                data=self._get_problem_choice_group_text("Problem Choice Group 1 Text", [("1", False), ('2', True)])
            ),
            XBlockFixtureDesc(
                "problem", "Problem Choice Group 2",
                data=self._get_problem_choice_group_text("Problem Choice Group 2 Text", [("Q", True), ('W', False)])
            ),
            XBlockFixtureDesc(
                "problem", "Problem Select 1",
                data=self._get_problem_select_text("Problem Select 1 Text", ["Option 1", "Option 2"], "Option 1")
            ),
            XBlockFixtureDesc(
                "problem", "Problem Select 2",
                data=self._get_problem_select_text("Problem Select 2 Text", ["Option 3", "Option 4"], "Option 4")
            ),
        )

    @property
    def _problem_headers(self):
        """ Expected XBLock headers according to populate_library_fixture """
        return frozenset(child.display_name.upper() for child in self.library_fixture.children)

    def _set_library_content_settings(self, count=1, capa_type="Any Type"):
        """
        Sets library content XBlock parameters, saves, publishes unit, goes to LMS unit page and
        gets children XBlock headers to assert against them
        """
        self._change_library_content_settings(count=count, capa_type=capa_type)
        self._auto_auth(self.USERNAME, self.EMAIL, False)
        self._goto_library_block_page()
        return self.library_content_page.children_headers

    def test_problem_type_selector(self):
        """
        Scenario: Ensure setting "Any Type" for Problem Type does not filter out Problems
        Given I have a library with two "Select Option" and two "Choice Group" problems, and a course containing
               LibraryContent XBlock configured to draw XBlocks from that library
        When I set library content xblock Problem Type to "Any Type" and Count to 3 and publish unit
        When I go to LMS courseware page for library content xblock as student
        Then I can see 3 xblocks from the library of any type
        When I set library content xblock Problem Type to "Choice Group" and Count to 1 and publish unit
        When I go to LMS courseware page for library content xblock as student
        Then I can see 1 xblock from the library of "Choice Group" type
        When I set library content xblock Problem Type to "Select Option" and Count to 2 and publish unit
        When I go to LMS courseware page for library content xblock as student
        Then I can see 2 xblock from the library of "Select Option" type
        When I set library content xblock Problem Type to "Matlab" and Count to 2 and publish unit
        When I go to LMS courseware page for library content xblock as student
        Then I can see 0 xblocks from the library
        """
        children_headers = self._set_library_content_settings(count=3, capa_type="Any Type")
        self.assertEqual(len(children_headers), 3)
        self.assertLessEqual(children_headers, self._problem_headers)

        # Choice group test
        children_headers = self._set_library_content_settings(count=1, capa_type="Multiple Choice")
        self.assertEqual(len(children_headers), 1)
        self.assertLessEqual(
            children_headers,
            set([header.upper() for header in ["Problem Choice Group 1", "Problem Choice Group 2"]])
        )

        # Choice group test
        children_headers = self._set_library_content_settings(count=2, capa_type="Dropdown")
        self.assertEqual(len(children_headers), 2)
        self.assertEqual(
            children_headers,
            set([header.upper() for header in ["Problem Select 1", "Problem Select 2"]])
        )

        # Missing problem type test
        children_headers = self._set_library_content_settings(count=2, capa_type="Custom Evaluated Script")
        self.assertEqual(children_headers, set())
