class AdvancedSettingsValidationTest(StudioCourseTest):
    """
    Tests for validation feature in Studio's advanced settings tab
    """
    course_name_key = 'Course Display Name'
    course_name_value = 'Test Name'

    def setUp(self):
        super(AdvancedSettingsValidationTest, self).setUp()
        self.advanced_settings = AdvancedSettingsPage(
            self.browser, self.course_info['org'], self.course_info['number'],
            self.course_info['run'])

        self.type_fields = [
            'Course Display Name', 'Advanced Module List',
            'Discussion Topic Mapping', 'Maximum Attempts',
            'Course Announcement Date'
        ]

        # Before every test, make sure to visit the page first
        self.advanced_settings.visit()

    def test_course_author_sees_default_advanced_settings(self):
        """
        Scenario: Test that advanced settings have the default settings
            Given a staff logs in to studio
            When this user goes to advanced settings page
                Then this user sees 'Allow Anonymous Discussion Posts' as true
                And 'Enable Timed Exams' as false
                And 'Maximum Attempts' as null
        """
        anonymous_discussion_setting = self.advanced_settings.get(
            'Allow Anonymous Discussion Posts')
        timed_exam_settings = self.advanced_settings.get('Enable Timed Exams')
        max_attempts = self.advanced_settings.get('Maximum Attempts')
        page_default_settings = [
            anonymous_discussion_setting, timed_exam_settings, max_attempts
        ]
        default_anonymous_discussion_setting = 'true'
        default_timed_exam_settings = 'false'
        default_max_attempts = 'null'
        expected_default_settings = [
            default_anonymous_discussion_setting, default_timed_exam_settings,
            default_max_attempts
        ]
        self.assertEqual(page_default_settings, expected_default_settings)

    def test_keys_appear_alphabetically(self):
        """
        Scenario: Test that advanced settings have all the keys in alphabetic order
            Given a staff logs in to studio
            When this user goes to advanced settings page
                Then he sees all the advanced setting keys in alphabetic order
        """

        key_names = self.advanced_settings.key_names
        self.assertEqual(key_names, sorted(key_names))

    def test_cancel_editing_key_value(self):
        """
        Scenario: Test that advanced settings does not save the key value, if cancel
        is clicked from notification bar
            Given a staff logs in to studio
            When this user goes to advanced settings page and enters and new course name
                Then he clicks 'cancel' buttin when asked to save changes
            When this user reloads the page
                And then he does not see any change in the original course name
        """

        original_course_display_name = self.advanced_settings.get(
            self.course_name_key)
        new_course_name = 'New Course Name'
        type_in_codemirror(self.advanced_settings, 16, new_course_name)
        self.advanced_settings.cancel()
        self.advanced_settings.refresh_and_wait_for_load()
        self.assertNotEqual(original_course_display_name, new_course_name, (
            'original course name:{} can not not be equal to unsaved course name {}'
            .format(original_course_display_name, new_course_name)))
        self.assertEqual(self.advanced_settings.get(
            self.course_name_key
        ), original_course_display_name, (
            'course name from the page should be same as original_course_display_name:{}'
            .format(original_course_display_name)))

    def test_editing_key_value(self):
        """
        Scenario: Test that advanced settings saves the key value, if save button
        is clicked from notification bar after the editing
            Given a staff logs in to studio
            When this user goes to advanced settings page and enters a new course name
                And he clicks 'save' button from the notification bar
                Then he is able to see the updated course name
        """
        new_course_name = ''.join(
            random.choice(string.ascii_uppercase) for _ in range(10))
        self.advanced_settings.set(self.course_name_key, new_course_name)
        self.assertEqual(
            self.advanced_settings.get(self.course_name_key),
            '"{}"'.format(new_course_name),
            ('course name from the page should be same as new_course_name:{}'.
             format(new_course_name)))

    def test_confirmation_is_shown_on_save(self):
        """
        Scenario: Test that advanced settings shows confirmation after editing a field successfully
            Given a staff logs in to studio
            When this user goes to advanced settings page and  edits any value
                And he clicks 'save' button from the notification bar
                Then he is able to see the confirmation message
        """
        self.advanced_settings.set('Maximum Attempts', 5)
        confirmation_message = self.advanced_settings.confirmation_message
        self.assertEqual(
            confirmation_message, 'Your policy changes have been saved.',
            'Settings must be saved successfully in order to have confirmation message'
        )

    def test_deprecated_settings_invisible_by_default(self):
        """
        Scenario: Test that advanced settings does not have deprecated settings by default
            Given a staff logs in to studio
            When this user goes to advanced settings page
                Then the user does not see the deprecated settings
                And sees 'Show Deprecated Settings' button
        """
        button_text = self.advanced_settings.deprecated_settings_button_text
        self.assertEqual(button_text, 'Show Deprecated Settings')
        self.assertFalse(
            self.advanced_settings.check_deprecated_settings_visibility())

    def test_modal_shows_one_validation_error(self):
        """
        Test that advanced settings don't save if there's a single wrong input,
        and that it shows the correct error message in the modal.
        """

        # Feed an integer value for String field.
        # .set method saves automatically after setting a value
        course_display_name = self.advanced_settings.get('Course Display Name')
        self.advanced_settings.set('Course Display Name', 1)
        self.advanced_settings.wait_for_modal_load()

        # Test Modal
        self.check_modal_shows_correct_contents(['Course Display Name'])
        self.advanced_settings.refresh_and_wait_for_load()

        self.assertEquals(
            self.advanced_settings.get('Course Display Name'),
            course_display_name,
            'Wrong input for Course Display Name must not change its value')

    def test_modal_shows_multiple_validation_errors(self):
        """
        Test that advanced settings don't save with multiple wrong inputs
        """

        # Save original values and feed wrong inputs
        original_values_map = self.get_settings_fields_of_each_type()
        self.set_wrong_inputs_to_fields()
        self.advanced_settings.wait_for_modal_load()

        # Test Modal
        self.check_modal_shows_correct_contents(self.type_fields)
        self.advanced_settings.refresh_and_wait_for_load()

        for key, val in original_values_map.iteritems():
            self.assertEquals(
                self.advanced_settings.get(key), val,
                'Wrong input for Advanced Settings Fields must not change its value'
            )

    def test_undo_changes(self):
        """
        Test that undo changes button in the modal resets all settings changes
        """

        # Save original values and feed wrong inputs
        original_values_map = self.get_settings_fields_of_each_type()
        self.set_wrong_inputs_to_fields()

        # Let modal popup
        self.advanced_settings.wait_for_modal_load()

        # Click Undo Changes button
        self.advanced_settings.undo_changes_via_modal()

        # Check that changes are undone
        for key, val in original_values_map.iteritems():
            self.assertEquals(self.advanced_settings.get(key), val,
                              'Undoing Should revert back to original value')

    def test_manual_change(self):
        """
        Test that manual changes button in the modal keeps settings unchanged
        """
        inputs = {
            "Course Display Name": 1,
            "Advanced Module List": 1,
            "Discussion Topic Mapping": 1,
            "Maximum Attempts": '"string"',
            "Course Announcement Date": '"string"',
        }

        self.set_wrong_inputs_to_fields()
        self.advanced_settings.wait_for_modal_load()
        self.advanced_settings.trigger_manual_changes()

        # Check that the validation modal went away.
        self.assertFalse(self.advanced_settings.is_validation_modal_present())

        # Iterate through the wrong values and make sure they're still displayed
        for key, val in inputs.iteritems():
            self.assertEquals(
                str(self.advanced_settings.get(key)), str(val),
                'manual change should keep: ' + str(val) + ', but is: ' +
                str(self.advanced_settings.get(key)))

    def check_modal_shows_correct_contents(self, wrong_settings_list):
        """
        Helper function that checks if the validation modal contains correct
        error messages.
        """
        # Check presence of modal
        self.assertTrue(self.advanced_settings.is_validation_modal_present())

        # List of wrong settings item & what is presented in the modal should be the same
        error_item_names = self.advanced_settings.get_error_item_names()
        self.assertEqual(set(wrong_settings_list), set(error_item_names))

        error_item_messages = self.advanced_settings.get_error_item_messages()
        self.assertEqual(len(error_item_names), len(error_item_messages))

    def get_settings_fields_of_each_type(self):
        """
        Get one of each field type:
           - String: Course Display Name
           - List: Advanced Module List
           - Dict: Discussion Topic Mapping
           - Integer: Maximum Attempts
           - Date: Course Announcement Date
        """
        return {
            "Course Display Name":
            self.advanced_settings.get('Course Display Name'),
            "Advanced Module List":
            self.advanced_settings.get('Advanced Module List'),
            "Discussion Topic Mapping":
            self.advanced_settings.get('Discussion Topic Mapping'),
            "Maximum Attempts":
            self.advanced_settings.get('Maximum Attempts'),
            "Course Announcement Date":
            self.advanced_settings.get('Course Announcement Date'),
        }

    def set_wrong_inputs_to_fields(self):
        """
        Set wrong values for the chosen fields
        """
        self.advanced_settings.set_values({
            "Course Display Name":
            1,
            "Advanced Module List":
            1,
            "Discussion Topic Mapping":
            1,
            "Maximum Attempts":
            '"string"',
            "Course Announcement Date":
            '"string"',
        })

    def test_only_expected_fields_are_displayed(self):
        """
        Scenario: The Advanced Settings screen displays settings/fields not specifically hidden from
        view by a developer.
        Given I have a set of CourseMetadata fields defined for the course
        When I view the Advanced Settings screen for the course
        The total number of fields displayed matches the number I expect
        And the actual fields displayed match the fields I expect to see
        """
        expected_fields = self.advanced_settings.expected_settings_names
        displayed_fields = self.advanced_settings.displayed_settings_names
        self.assertEquals(set(displayed_fields), set(expected_fields))