import json
import random

from institute.models import Institute, InstituteJWT
from plan.models import Environment
from question_bank.models import QuestionBank
from wikiazma.myTest import MyTestCase
from .models import Question, QUESTION_TYPE_MULTIPLE_CHOICE, QUESTION_TYPE_DESCRIPTIVE


class QuestionTest(MyTestCase):

    def setUp(self):
        self.setBaseUser()
        self.setUpHelpers()

        Environment.objects.update(default_max_institutes_per_user=100, default_max_collaborators_per_institute=100)
        self.institute = self.instituteHelper.create_institute(name="unit_test_institute")

        self.question_bank_id = self.questionbankHelper.create_question_bank(self.institute['id'], name='my_question_bank')

    def tearDown(self):
        InstituteJWT.objects.all().delete()
        Institute.objects.all().delete()
        QuestionBank.objects.all().delete()
        Question.objects.all().delete()

    def test_create_question_serializer_error(self):
        # sending no request data
        (response_data, status_code, error_message) = self.post_with_token(self.questionHelper.create_question_url)
        self.assertEqual(400, status_code, error_message)
        self.assertIn('question_bank_id', response_data['fields'], error_message)
        self.assertEqual('This field is required.', response_data['fields']['question_bank_id'], error_message)
        # keywords is optional now
        # self.assertIn('keywords', response_data['fields'], error_message)
        # self.assertEqual('This field is required.',
        #                  response_data['fields']['keywords'], error_message)

        # sending only question_bank_id
        request = {'question_bank_id': self.non_existence_uuid}
        (response_data, status_code, error_message) = self.post_with_token(self.questionHelper.create_question_url, request)
        self.assertEqual(400, status_code,
                         error_message)  # self.assertIn('keywords', response_data['fields'], error_message)  # self.assertEqual('This field is required.',  #                  response_data['fields']['keywords'], error_message)

    def test_create_question_wrong_answer_type(self):
        input_rules = {'type': 'wrongType'}

        (response_data, status_code, error_message) = self.questionHelper.create_question_with_defaults(self.question_bank_id,
                                                                                                        input_rules=input_rules)

        self.assertEqual(400, status_code, error_message)
        self.assertEqual('validation_error', response_data['status'], error_message)
        self.assertEqual('Some fields are not valid', response_data['message'], error_message)

    def test_multiple_choice_question_wrong_answer_rules_wrong_types(self):
        # answer rules with wrong id type
        # TODO
        # self.questionHelper.wrong_answer_rule_checker(self.question_bank_id,
        #                                               input_rules={"max_selectable_choices": 2},
        #                                               choices={
        #                                                   1234567: "...",
        #                                                   "strID": "...",
        #                                               },
        #                                               correct_choices=[1234567],
        #                                               expected_error_field_name='choices',
        #                                               expected_error="valid 'id' was not found in one of the items in the choices. Each item in choices must only contain id(int)*, answer_text(str)*, answer_text_format(str)*, is_correct_choice(bool)*, priority(int)*.")

        # answer rules with wrong correct_choices
        self.questionHelper.wrong_answer_rule_checker(self.question_bank_id, input_rules={"max_selectable_choices": 2},
                                                      choices={1234567: "...", 1234568: "...", }, correct_choices=[1111111],
                                                      expected_error_field_name='correct_choices',
                                                      expected_error="Invalid correct choice id (1111111)")

        # input_rules without max_selectable_choices
        self.questionHelper.wrong_answer_rule_checker(self.question_bank_id, input_rules={},
                                                      choices={1234567: "...", 1234568: "...", }, correct_choices=[1234567],
                                                      expected_error_field_name='input_rules',
                                                      expected_error="valid 'max_selectable_choices' was not found in input_rules. input_rules must only contain max_selectable_choices(int)*.")

        # answer rules with wrong max_selectable_choices
        self.questionHelper.wrong_answer_rule_checker(self.question_bank_id, input_rules={"max_selectable_choices": 0},
                                                      choices={1234567: "...", 1234568: "...", }, correct_choices=[1234567],
                                                      expected_error_field_name='input_rules',
                                                      expected_error="max_selectable_choices should be an integer between 1 and number of choices")

        # answer rules with wrong max_selectable_choices
        self.questionHelper.wrong_answer_rule_checker(self.question_bank_id, input_rules={"max_selectable_choices": 3},
                                                      choices={1234567: "...", 1234568: "...", }, correct_choices=[1234567],
                                                      expected_error_field_name='input_rules',
                                                      expected_error="max_selectable_choices should be an integer between 1 and number of choices")

        # answer rules with only one choice
        self.questionHelper.wrong_answer_rule_checker(self.question_bank_id, input_rules={"max_selectable_choices": 1},
                                                      choices={12345678: "...", }, correct_choices=[12345678],
                                                      expected_error_field_name='choices',
                                                      expected_error='choices should be a dict of at least 2 and at most 10 children')

    def test_create_wrong_question_type_error(self):
        (response_data, status_code, error_message) = self.questionHelper.create_question_with_defaults(self.question_bank_id,
                                                                                                        question_type="invalid")

        self.assertEqual(400, status_code, error_message)
        self.assertEqual('Some fields are not valid', response_data['message'], error_message)

    def test_create_multiple_choice_question_successful(self):
        input_rules = {"max_selectable_choices": 2}

        tags = {"subject": "math", "lesson": "1"}

        choices = {"12345999": "...", "12345111": "...", "12345555": "..."}

        correct_choices = ["12345999", "12345555"]

        (response_data, status_code, error_message) = self.questionHelper.new_question(self.question_bank_id,
                                                                                       input_rules=input_rules, choices=choices,
                                                                                       correct_choices=correct_choices, tags=tags)

        self.assertEqual("ok", response_data['status'])
        self.assertEqual(True, response_data['data']['question']['auto_correctable'], error_message)

        for key, value in tags.items():
            self.assertEqual(value, response_data['data']['question']['tags'][key])

        for index, (key, value) in enumerate(choices.items()):
            self.assertEqual(value, response_data['data']['question']['choices'][key])
            # check choice orders
            self.assertEqual(index, list(response_data['data']['question']['choices']).index(key))

        for correct_choice in correct_choices:
            self.assertIn(correct_choice, response_data['data']['question']['correct_choices'])

        question_bank = QuestionBank.objects.get(id=self.question_bank_id)
        self.assertEqual(1, question_bank.questions_count)

        # TODO check question tags

    def test_create_descriptive_question_successful(self):
        input_rules = json.dumps({'max_characters': 1000, "attachment": False})

        (response_data, status_code, error_message) = self.questionHelper.new_question(self.question_bank_id,
                                                                                       question_type='descriptive',
                                                                                       input_rules=input_rules)

        # self.assertEqual('my_question_bank', response_data['data']['question']['question_bank'], error_message)
        self.assertEqual(False, response_data['data']['question']['auto_correctable'], error_message)

        question_bank = QuestionBank.objects.get(id=self.question_bank_id)
        self.assertEqual(1, question_bank.questions_count)

        # TODO check question tags

    def test_create_descriptive_question_error(self):
        input_rules = json.dumps({"attachment": False})

        (response_data, status_code, error_message) = self.questionHelper.new_question(self.question_bank_id,
                                                                                       question_type='descriptive',
                                                                                       input_rules=input_rules,
                                                                                       status_200=False)

        self.assertEqual(400, status_code, error_message)
        self.assertEqual(
            'valid \'max_characters\' was not found in input_rules. input_rules must only contain max_characters(int)*, attachment(bool)*.',
            response_data['fields']['input_rules'], error_message)

    def test_bulk_create_question_successful(self):
        input_rules = {"max_selectable_choices": 2}

        choices = {12345678: "...", 12345677: "...", 12345676: "..."}

        correct_choices = [12345678, 12345676]

        questions = []
        random_question_count = random.randint(3, 15)
        for i in range(random_question_count):
            questions.append({'question_text': 'question_text', 'format': 'markdownV1', 'question_type': 'multiple-choice',
                              'input_rules': input_rules, 'choices': choices, 'correct_choices': correct_choices,
                              'tags': {'subject': 'math'}, })

        request = {'question_bank_id': self.question_bank_id, 'questions': questions}

        (response_data, status_code, error_message) = self.post_with_token(self.questionHelper.bulk_create_question_url, request,
                                                                           application_json=True)
        self.assertEqual(200, status_code, error_message)
        self.assertEqual('ok', response_data['status'], error_message)

        question_bank = QuestionBank.objects.get(id=self.question_bank_id)
        self.assertEqual(random_question_count, question_bank.questions_count)

    def test_bulk_create_question_serializer_error(self):
        questions = []
        # sending almost no data except keywords
        for i in range(5):
            questions.append({'keywords': 'solve x^2-2x+1=0'})

        request = {'question_bank_id': self.question_bank_id, 'questions': questions}

        (response_data, status_code, error_message) = self.post_with_token(self.questionHelper.bulk_create_question_url, request,
                                                                           application_json=True)
        self.assertEqual(400, status_code, error_message)
        self.assertIn('questions', response_data['fields'], error_message)
        self.assertIn('question_text', response_data['fields']['questions'], error_message)
        # TODO why is this an array?
        self.assertEqual(['This field is required.'], response_data['fields']['questions']['question_text'], error_message)

        question_bank = QuestionBank.objects.get(id=self.question_bank_id)
        # no question should be saved
        self.assertEqual(0, question_bank.questions_count)

    def test_delete_question(self):
        (response_data, status_code, error_message) = self.questionHelper.new_question(self.question_bank_id)
        question_id = response_data['data']['question']['id']
        question_bank = QuestionBank.objects.get(id=self.question_bank_id)
        self.assertEqual(1, question_bank.questions_count)

        self.questionHelper.archive_question_helper(self.question_bank_id, question_id)

    def test_list_questions_serializer_error(self):
        # sending no request data
        request = {}
        (response_data, status_code, error_message) = self.post_with_token(self.questionHelper.list_question_url, request)
        self.assertEqual(400, status_code, error_message)
        self.assertEqual(response_data['fields']['question_bank_id'], 'This field is required.', error_message)
        self.assertEqual(response_data['fields']['skip'], 'This field is required.', error_message)
        self.assertEqual(response_data['fields']['take'], 'This field is required.', error_message)

    def test_list_questions_successful_1(self):
        self.questionHelper.check_question_bank_number_of_questions(question_bank_id=self.question_bank_id, num_questions=0,
                                                                    context_institute_id=self.institute['id'])

        num_questions = random.randint(1, 10)
        for i in range(num_questions):
            self.questionHelper.new_question(self.question_bank_id)

        self.questionHelper.check_question_bank_number_of_questions(self.question_bank_id, num_questions,
                                                                    context_institute_id=self.institute['id'])

    def test_list_questions_successful_2(self):
        self.questionHelper.new_question(self.question_bank_id, keywords='quiz')
        self.questionHelper.new_question(self.question_bank_id, question_type='descriptive')
        # new question with tags=[]
        self.questionHelper.new_question(self.question_bank_id, question_type='descriptive', tags={})

        # test non-existence q
        request = {'question_bank_id': self.question_bank_id, 'context_institute_id': self.institute['id'], 'skip': 0, 'take': 10,
                   'archived_filter': 'include', 'sort_by': 'keywords_asc', 'q': 'some other words',
                   'auto_correctable_filter': 'include'}
        (response_data, status_code, error_message) = self.post_with_token(self.questionHelper.list_question_url, request)
        self.assertEqual(200, status_code, error_message)
        self.assertEqual(0, len(response_data['data']['questions']), error_message)

        # test existence q
        request = {'question_bank_id': self.question_bank_id, 'context_institute_id': self.institute['id'], 'skip': 0, 'take': 10,
                   'archived_filter': 'include', 'sort_by': 'keywords_asc', 'q': 'quiz', 'auto_correctable_filter': 'include'}
        (response_data, status_code, error_message) = self.post_with_token(self.questionHelper.list_question_url, request)
        self.assertEqual(200, status_code, error_message)
        self.assertEqual(1, len(response_data['data']['questions']), error_message)
        self.assertEqual('quiz', response_data['data']['questions'][0]['keywords'], error_message)

        # test question_type descriptive
        request = {'question_bank_id': self.question_bank_id, 'context_institute_id': self.institute['id'], 'skip': 0, 'take': 10,
                   'archived_filter': 'include', 'sort_by': 'keywords_asc', 'question_type': QUESTION_TYPE_DESCRIPTIVE,
                   'auto_correctable_filter': 'include'}
        (response_data, status_code, error_message) = self.post_with_token(self.questionHelper.list_question_url, request)
        self.assertEqual(200, status_code, error_message)
        self.assertEqual(2, len(response_data['data']['questions']), error_message)

        # test question_type multiple_choice
        request = {'question_bank_id': self.question_bank_id, 'context_institute_id': self.institute['id'], 'skip': 0, 'take': 10,
                   'archived_filter': 'include', 'sort_by': 'keywords_asc', 'question_type': QUESTION_TYPE_MULTIPLE_CHOICE,
                   'auto_correctable_filter': 'include'}
        (response_data, status_code, error_message) = self.post_with_token(self.questionHelper.list_question_url, request)
        self.assertEqual(200, status_code, error_message)
        self.assertEqual(1, len(response_data['data']['questions']), error_message)

        # sending an ordinary request with old_tag_filters=[] and tag_filters=[]
        request = {'question_bank_id': self.question_bank_id, 'context_institute_id': self.institute['id'], 'skip': 0, 'take': 10,
                   'archived_filter': 'include', 'sort_by': 'keywords_asc', 'question_type': QUESTION_TYPE_MULTIPLE_CHOICE,
                   'auto_correctable_filter': 'include', 'old_tag_filters': json.dumps([]), 'tag_filters': json.dumps([])}
        (response_data, status_code, error_message) = self.post_with_token(self.questionHelper.list_question_url, request)
        self.assertEqual(200, status_code, error_message)
        self.assertEqual(1, len(response_data['data']['questions']), error_message)

        # creating some other questions with tags
        self.questionHelper.new_question(self.question_bank_id, tags=json.dumps({'hardness': '1'}))
        self.questionHelper.new_question(self.question_bank_id, tags=json.dumps({'subject': 'math', 'hardness': '2'}))

        # test auto_correctable_filter: include
        request = {'question_bank_id': self.question_bank_id, 'context_institute_id': self.institute['id'], 'skip': 0, 'take': 10,
                   'archived_filter': 'include', 'sort_by': 'keywords_asc', 'auto_correctable_filter': "include"}
        (response_data, status_code, error_message) = self.post_with_token(self.questionHelper.list_question_url, request)
        self.assertEqual(200, status_code, error_message)
        self.assertEqual(5, len(response_data['data']['questions']), error_message)

        # test auto_correctable_filter: exclude
        request = {'question_bank_id': self.question_bank_id, 'context_institute_id': self.institute['id'], 'skip': 0, 'take': 10,
                   'archived_filter': 'include', 'sort_by': 'keywords_asc', 'auto_correctable_filter': "exclude"}
        (response_data, status_code, error_message) = self.post_with_token(self.questionHelper.list_question_url, request)
        self.assertEqual(200, status_code, error_message)
        self.assertEqual(2, len(response_data['data']['questions']), error_message)

        # test auto_correctable_filter: only
        request = {'question_bank_id': self.question_bank_id, 'context_institute_id': self.institute['id'], 'skip': 0, 'take': 10,
                   'archived_filter': 'include', 'sort_by': 'keywords_asc', 'auto_correctable_filter': "only"}
        (response_data, status_code, error_message) = self.post_with_token(self.questionHelper.list_question_url, request)
        self.assertEqual(200, status_code, error_message)
        self.assertEqual(3, len(response_data['data']['questions']), error_message)

        # test sort_by: random
        request = {'question_bank_id': self.question_bank_id, 'context_institute_id': self.institute['id'], 'skip': 0, 'take': 10,
                   'archived_filter': 'include', 'sort_by': 'random', 'auto_correctable_filter': "only"}
        (response_data, status_code, error_message) = self.post_with_token(self.questionHelper.list_question_url, request)
        self.assertEqual(200, status_code, error_message)
        self.assertEqual(3, len(response_data['data']['questions']), error_message)

    def test_list_questions_successful_old_tag_filters(self):
        # creating questions
        self.questionHelper.new_question(self.question_bank_id, tags=json.dumps({'hardness': '1'}))
        self.questionHelper.new_question(self.question_bank_id, keywords='my_test_question',
                                         tags=json.dumps({'subject': 'math', 'hardness': '2'}))

        request = {'question_bank_id': self.question_bank_id, 'context_institute_id': self.institute['id'], 'skip': 0, 'take': 10,
                   'archived_filter': 'include', 'sort_by': 'keywords_asc', 'auto_correctable_filter': 'include',
                   'old_tag_filters': json.dumps([[{'key': 'subject', 'value': 'math'}], [{'key': 'hardness', 'value': '2'}]])}
        (response_data, status_code, error_message) = self.post_with_token(self.questionHelper.list_question_url, request)
        self.assertEqual(200, status_code, error_message)
        self.assertEqual(1, len(response_data['data']['questions']), error_message)
        self.assertEqual('my_test_question', response_data['data']['questions'][0]['keywords'], error_message)

        # sending a non_existence tag
        request = {'question_bank_id': self.question_bank_id, 'context_institute_id': self.institute['id'], 'skip': 0, 'take': 10,
                   'archived_filter': 'include', 'sort_by': 'keywords_asc', 'auto_correctable_filter': 'include',
                   'old_tag_filters': json.dumps([[{'key': 'subject', 'value': 'math'}], [{'key': 'hardness', 'value': '3'}]])}
        (response_data, status_code, error_message) = self.post_with_token(self.questionHelper.list_question_url,
                                                                           request)
        self.assertEqual(200, status_code, error_message)
        self.assertEqual(0, len(response_data['data']['questions']), error_message)

    def test_list_questions_successful_new_tag_filters(self):
        # creating questions
        self.questionHelper.create_questions_with_real_tags(self.question_bank_id)

        # گرفتن تمام سوالات فصل1و2
        self.questionHelper.tag_filter_helper(self.question_bank_id, self.institute['id'], [
            {
                'key': 'فصل',
                'values': [
                    {'value': '1', 'children': []},
                    {'value': '2', 'children': []}
                ]
            }
        ], ['q1_1_1', 'q1_2_1', 'q1_2_2', 'q1_3_1', 'q2_1_1', 'q2_2_1', 'q2_2_2', 'q2_3_1'])

        # گرفتن تمام سوالات فصل1 درس 1 و 2 (درس 3 را نمیخواهیم)
        self.questionHelper.tag_filter_helper(self.question_bank_id, self.institute['id'], [
            {
                'key': 'فصل',
                'values': [
                    {'value': '1', 'children': [
                        {
                            'key': 'درس',
                            'values': [
                                {'value': '1', 'children': []},
                                {'value': '2', 'children': []},
                            ]
                        }
                    ]},
                ]
            }
        ], ['q1_1_1', 'q1_2_1', 'q1_2_2'])

        # گرفتن تمام سوالات فصل1 درس 1 و 3 با درجه سختی آسان روش اول
        self.questionHelper.tag_filter_helper(self.question_bank_id, self.institute['id'], [
            {
                'key': 'فصل',
                'values': [
                    {'value': '1', 'children': [
                        {'key': 'درس',
                         'values': [
                             {'value': '1', 'children': []},
                             {'value': '3', 'children': []},
                         ]},
                        {
                            'key': 'درجه سختی',
                            'values': [
                                {'value': 'آسان', 'children': []},
                            ]
                        }
                    ]},
                ]
            }
        ], ['q1_1_1', 'q1_3_1'])

        # گرفتن تمام سوالات فصل1 درس 1 و 3 با درجه سختی آسان روش دوم
        self.questionHelper.tag_filter_helper(self.question_bank_id, self.institute['id'], [
            {
                'key': 'فصل',
                'values': [
                    {'value': '1', 'children': [
                        {
                            'key': 'درس',
                            'values': [
                                {'value': '1', 'children': []},
                                {'value': '3', 'children': []},
                            ]
                        }
                    ]},
                ]
            },
            {
                'key': 'درجه سختی',
                'values': [
                    {'value': 'آسان', 'children': []},
                ]
            }
        ], ['q1_1_1', 'q1_3_1'])

        # گرفتن سوالات فصل 1 درس 2 مبحث گرامر و سوالات فصل 2 درس 2 مبحث کلمه
        self.questionHelper.tag_filter_helper(self.question_bank_id, self.institute['id'], [
            {
                'key': 'فصل',
                'values': [
                    {'value': '1', 'children': [
                        {
                            'key': 'درس',
                            'values': [
                                {'value': '2', 'children': [
                                    {
                                        'key': 'مبحث',
                                        'values': [
                                            {'value': 'گرامر', 'children': []},
                                        ]
                                    }
                                ]},
                            ]
                        }
                    ]},
                    {'value': '2', 'children': [
                        {
                            'key': 'درس',
                            'values': [
                                {'value': '2', 'children': [
                                    {
                                        'key': 'مبحث',
                                        'values': [
                                            {'value': 'کلمه', 'children': []},
                                        ]
                                    }
                                ]},
                            ]
                        }
                    ]},
                ]
            }
        ], ['q1_2_1', 'q2_2_2'])

    def test_list_questions_error(self):
        # not sending archived_filter
        request = {'question_bank_id': self.question_bank_id, 'skip': 0, 'take': 10, 'sort_by': 'keywords_asc',
                   'auto_correctable_filter': "only"}
        (response_data, status_code, error_message) = self.post_with_token(self.questionHelper.list_question_url, request)
        self.assertEqual(400, status_code, error_message)
        self.assertEqual('This field is required.', response_data['fields']['archived_filter'], error_message)

        # sending invalid archived_filter
        request = {'question_bank_id': self.question_bank_id, 'skip': 0, 'take': 10, 'archived_filter': 'non_existence_filter',
                   'sort_by': 'keywords_asc', 'auto_correctable_filter': "only"}
        (response_data, status_code, error_message) = self.post_with_token(self.questionHelper.list_question_url, request)
        self.assertEqual(400, status_code, error_message)
        self.assertEqual('archived_filter should be one of include, exclude, only', response_data['fields']['archived_filter'],
                         error_message)

        # not sending auto_correctable_filter
        request = {'question_bank_id': self.question_bank_id, 'skip': 0, 'take': 10, 'archived_filter': 'include',
                   'sort_by': 'keywords_asc'}
        (response_data, status_code, error_message) = self.post_with_token(self.questionHelper.list_question_url, request)
        self.assertEqual(400, status_code, error_message)
        self.assertEqual('This field is required.', response_data['fields']['auto_correctable_filter'], error_message)

        # sending invalid auto_correctable_filter
        request = {'question_bank_id': self.question_bank_id, 'skip': 0, 'take': 10, 'archived_filter': 'include',
                   'sort_by': 'keywords_asc', 'auto_correctable_filter': "non_existence_filter"}
        (response_data, status_code, error_message) = self.post_with_token(self.questionHelper.list_question_url, request)
        self.assertEqual(400, status_code, error_message)
        self.assertEqual('auto_correctable_filter should be one of include, exclude, only',
                         response_data['fields']['auto_correctable_filter'], error_message)

    def test_get_question_successful(self):
        (response_data, status_code, error_message) = self.questionHelper.new_question(self.question_bank_id,
                                                                                       tags=json.dumps({"tag_key": "tag_value"}))
        question_id = response_data['data']['question']['id']
        (response_data, status_code, error_message) = self.post_with_token(self.questionHelper.get_question_url,
                                                                           {'question_id': question_id,
                                                                            'context_institute_id': self.institute['id']})
        self.assertEqual(200, status_code, error_message)
        self.assertEqual('ok', response_data['status'], error_message)
        self.assertIn('question', response_data['data'], error_message)
        self.assertIn('keywords', response_data['data']['question'], error_message)
        self.assertIn('auto_correctable', response_data['data']['question'], error_message)
        self.assertIn('question_text', response_data['data']['question'], error_message)
        self.assertIn('format', response_data['data']['question'], error_message)
        self.assertIn('question_type', response_data['data']['question'], error_message)
        self.assertIn('input_rules', response_data['data']['question'], error_message)
        self.assertIn('choices', response_data['data']['question'], error_message)
        self.assertIn('correct_choices', response_data['data']['question'], error_message)
        self.assertIn('max_selectable_choices', response_data['data']['question']['input_rules'], error_message)
        self.assertIn('tags', response_data['data']['question'], error_message)
        self.assertEqual(1, len(response_data['data']['question']['tags']), error_message)
        self.assertEqual(dict, type(response_data['data']['question']['tags']), error_message)
        self.assertIn('tag_key', response_data['data']['question']['tags'], error_message)

        self.assertEqual('tag_value', response_data['data']['question']['tags']['tag_key'], error_message)

    def test_edit_question_successful(self):
        # tag_value1 will be deleted
        # tag_key2 will remain
        # tag_key3 will be added later
        (response_data, status_code, error_message) = self.questionHelper.new_question(self.question_bank_id, tags=json.dumps(
            {"tag_key1": "tag_value1", "tag_key2": "tag_value2"}))
        question = response_data['data']['question']
        question_id = question["id"]
        new_choices = json.dumps({"123456": "1", "123457": "2", "123458": "3", "123459": "4", })
        new_tags = json.dumps({"tag_key3": "tag_value3", "tag_key1": "tag_value1"})
        new_keywords = "new_keywords"
        new_question_text = "new_question_text"
        new_format = "jsonV1"
        new_input_rules = json.dumps({"max_selectable_choices": 1})
        new_solution = "new_solution"
        new_question_type = "multiple-choice"
        new_correct_choices = ["123458"]

        request_payload = {"question_id": question_id, "choices": new_choices, "tags": new_tags, "keywords": new_keywords,
                           "question_text": new_question_text, "format": new_format, "input_rules": new_input_rules,
                           "solution": new_solution, "question_type": new_question_type, "correct_choices": new_correct_choices}

        # edit the question to delete some tags from it
        (response_data, status_code, error_message) = self.post_with_token(self.questionHelper.edit_question_url, request_payload)
        self.assertEqual(200, status_code, error_message)
        self.assertEqual('ok', response_data['status'], error_message)
        self.assertIn('question', response_data['data'], error_message)
        self.assertIn('keywords', response_data['data']['question'], error_message)
        self.assertIn('auto_correctable', response_data['data']['question'], error_message)
        self.assertIn('question_text', response_data['data']['question'], error_message)
        self.assertIn('format', response_data['data']['question'], error_message)
        self.assertIn('question_type', response_data['data']['question'], error_message)
        self.assertIn('input_rules', response_data['data']['question'], error_message)
        self.assertIn('choices', response_data['data']['question'], error_message)
        self.assertIn('correct_choices', response_data['data']['question'], error_message)
        self.assertIn('max_selectable_choices', response_data['data']['question']['input_rules'], error_message)
        self.assertIn('tags', response_data['data']['question'], error_message)
        self.assertEqual(2, len(response_data['data']['question']['tags']), error_message)
        self.assertEqual(dict, type(response_data['data']['question']['tags']), error_message)
        self.assertIn('tag_key1', response_data['data']['question']['tags'], error_message)
        self.assertNotIn('tag_key2', response_data['data']['question']['tags'], error_message)
        self.assertIn('tag_key3', response_data['data']['question']['tags'], error_message)

        self.assertEqual('tag_value1', response_data['data']['question']['tags']['tag_key1'], error_message)
        self.assertEqual('tag_value3', response_data['data']['question']['tags']['tag_key3'], error_message)

        # creating a new descriptive question
        (response_data, status_code, error_message) = self.questionHelper.new_question(self.question_bank_id, tags=json.dumps(
            {"tag_key1": "tag_value2", "tag_key2": "tag_value3"}), question_type=QUESTION_TYPE_DESCRIPTIVE)

        # editing the question to a multiple-choice question
        question = response_data['data']['question']
        question_id = question['id']
        input_rules = json.dumps({"max_selectable_choices": 2})
        choices = json.dumps({"12345678": "...", "12345677": "...", "12345676": "..."})
        new_format = question['format']
        new_question_text = question['question_text']
        correct_choices = [12345678, 12345676]
        (response_data, status_code, error_message) = self.post_with_token(self.questionHelper.edit_question_url,
                                                                           {'question_id': question_id,
                                                                            'question_type ': QUESTION_TYPE_MULTIPLE_CHOICE,
                                                                            'question_text ': new_question_text,
                                                                            'input_rules ': input_rules, 'choices': choices,
                                                                            'correct_choices': correct_choices,
                                                                            "format": new_format, 'tags': json.dumps(
                                                                               {"tag_key3": "tag_value5",
                                                                                "tag_key1": "tag_value7"})})
        self.assertEqual(200, status_code, error_message)
        self.assertEqual('ok', response_data['status'], error_message)
        self.assertIn('multiple-choice', response_data['data']['question']['question_type'], error_message)

    def test_edit_question_error(self):
        (response_data, status_code, error_message) = self.questionHelper.new_question(self.question_bank_id)

        # editing the question without sending choices

        # This test is no longer suitable because all fields are required
        # question_id = response_data['data']['question']['id']
        # input_rules = json.dumps({"max_selectable_choices": 2})
        # correct_choices = [12345678, 12345676]
        # (response_data, status_code, error_message) = self.post_with_token(self.questionHelper.edit_question_url,
        #                                                                    {'question_id': question_id,
        #                                                                     'input_rules ': input_rules,
        #                                                                     'correct_choices': correct_choices})
        # self.assertEqual(400, status_code, error_message)
        # self.assertEqual('validation_error', response_data['status'], error_message)
        # self.assertIn('input_rules', response_data['fields'], error_message)
        # self.assertEqual("input_rules, choices, and correct_choices should all have a value or should all be null",
        #                  response_data['fields']['input_rules'], error_message)

        # edit a deleted question
        (response_data, status_code, error_message) = self.questionHelper.new_question(self.question_bank_id, tags=json.dumps(
            {"tag_key1": "tag_value1", "tag_key2": "tag_value2"}))
        question = response_data['data']['question']

        question = response_data['data']['question']
        question_id = question["id"]
        new_choices = json.dumps({"123456": "1", "123457": "2", "123458": "3", "123459": "4", })
        new_tags = json.dumps({"tag_key3": "tag_value3", "tag_key1": "tag_value1"})
        new_keywords = "new_keywords"
        new_question_text = question["question_text"]
        new_format = question["format"]
        new_input_rules = json.dumps({"max_selectable_choices": 1})
        new_solution = question["solution"]
        new_question_type = question["question_type"]
        new_correct_choices = ["123457"]

        request_payload = {"question_id": question_id, "choices": new_choices, "tags": new_tags, "keywords": new_keywords,
                           "question_text": new_question_text, "format": new_format, "input_rules": new_input_rules,
                           "solution": new_solution, "question_type": new_question_type, "correct_choices": new_correct_choices, }

        self.questionHelper.archive_question_helper(self.question_bank_id, question_id)
        (response_data, status_code, error_message) = self.post_with_token(self.questionHelper.edit_question_url, request_payload)
        self.assertEqual(400, status_code, error_message)
        self.assertEqual('error', response_data['status'], error_message)
        self.assertEqual('An archived question cannot be edited.', response_data['message'], error_message)
