import json

from quiz.models import QuizSheet, QUIZ_SHEET_STATE_NOT_ATTENDED, QUIZ_SHEET_STATE_IN_PROGRESS, QuestionScore

ENTRANCE_TYPE_PUBLIC = 'public'
ENTRANCE_TYPE_API_INVITE = 'API_invite'
ALLOWED_ENTRANCE_TYPES = [ENTRANCE_TYPE_PUBLIC, ENTRANCE_TYPE_API_INVITE]

REFERENCED_QUESTION = 'REFERENCED_QUESTION'
QUERY_QUESTION = 'QUERY_QUESTION'
EXACT_QUESTION = 'EXACT_QUESTION'


class QuizTestHelper:
    create = "/quiz/create"
    edit = "/quiz/edit"
    archive = "/quiz/archive"
    list = "/quiz/list"
    get = "/quiz/get"
    gateway = "/quiz/gateway"
    enter = "/quiz/enter"
    generate_quiz_report = "/quiz/generate_quiz_report"
    set_answer = "/quiz/set_answer"
    finish = "/quiz/finish"
    report = "/quiz/sheet/report"
    invoice = "/quiz/invoice"
    stat = "/quiz/stat"
    sheet_list = "/quiz/sheet/list"
    attended_sheet_list = "/quiz/sheet/attended/list"
    attended_sheet_get = "/quiz/sheet/attended/get"
    create_quiz_template = "/quiz/template/create"
    edit_quiz_template = '/quiz/template/edit'
    list_quiz_template = "/quiz/template/list"
    market_quiz_template = "/quiz/template/market"
    get_quiz_template = "/quiz/template/get"
    generate_exam_page = "/quiz/institute_student/sheet/generate"
    pause_exam = "/exam/exam/pause"
    extend_exam = "/exam/exam/extend"
    taken_exam_page_list = '/exam/attended_exam_page/list'
    exam_page_get = '/exam/exam/page/get'
    quiz_sheet_correction_add = '/quiz/sheet/correction/add'
    exam_page_correction_publish = '/exam/exam/page/correction/publish'
    date_time_format = '%Y-%m-%dT%H:%M:%S'

    referenced_question_institute_id = None
    referenced_question_retrievers = []

    exact_question_institute_id = None
    exact_question_retrievers = []

    query_question_institute_id = None
    query_question_retrievers = []

    def generate_referenced_question_retrievers(self, institute_id, force_clear=False, public=False, token=None,
                                                question_type='multiple-choice'):

        if force_clear:
            self.referenced_question_retrievers.clear()
        if len(self.referenced_question_retrievers) == 0 or self.referenced_question_institute_id != institute_id:
            self.referenced_question_retrievers.clear()
            question_bank_id = self.tester.questionbankHelper.create_question_bank(institute_id, public=public, token=token)
            for i in range(3):
                (response_data, status_code, error_message) = self.tester.questionHelper.create_question_with_defaults(
                    question_bank_id, token=token, question_type=question_type)
                self.tester.assertEqual(200, status_code, error_message)
                question_id = response_data['data']['question']['id']
                self.referenced_question_retrievers.append({
                    # Comment id #68246256; scores for 3 questions, the choices are in self.tester.questionHelper.create_question_with_defaults
                    "insert_type": REFERENCED_QUESTION, "positive_score": 2.5, "negative_score": 1.0, "duration": 20.0,
                    "question_id": question_id, })

    def generate_exact_question_retrievers(self, institute_id, force_clear=False, question_type='multiple-choice'):

        if force_clear:
            self.exact_question_retrievers.clear()
        if len(self.exact_question_retrievers) == 0 or self.exact_question_institute_id != institute_id:
            self.exact_question_retrievers.clear()
            for i in range(3):
                self.exact_question_retrievers.append(
                    {"insert_type": EXACT_QUESTION, "positive_score": 2.5, "negative_score": 1.0, "duration": 20.0,
                     "data": {'question_text': 'abc', 'question_type': question_type, 'format': 'htmlV1',
                              'input_rule': {'max_selectable_choices': 1} if question_type == 'multiple-choice' else {
                                  'max_characters': 100, 'attachment': False}, 'solution': 'def'}})

    def generate_query_question_retrievers(self, institute_id, question_type='multiple-choice'):
        if len(self.query_question_retrievers) == 0 or self.referenced_question_institute_id != institute_id:
            self.query_question_retrievers.clear()
            question_bank_id = self.tester.questionbankHelper.create_question_bank(institute_id)

            # creating a question that should not be found in the generated_exam
            self.tester.questionHelper.create_question_with_defaults(question_bank_id, tags=json.dumps({'subject': 'physics'}),
                                                                     question_type=question_type)

            # creating questions that should be found in the generated_exam
            for i in range(5):
                self.tester.questionHelper.create_question_with_defaults(question_bank_id, tags=json.dumps({'subject': 'math'}),
                                                                         question_type=question_type)

            for i in range(2):
                self.query_question_retrievers.append(
                    {"insert_type": QUERY_QUESTION, "positive_score": 2.5, "negative_score": 1.0, "duration": 20.0, "count": 3,
                     "question_bank_id": question_bank_id, "tag_filters": [[{'key': 'subject', 'value': 'math'}]],
                     "type_filter": 'descriptive'})

    def get_general_rule(self, institute_id):
        self.generate_referenced_question_retrievers(institute_id)
        self.generate_exact_question_retrievers(institute_id)
        self.generate_query_question_retrievers(institute_id)

        return {'retrievers': self.referenced_question_retrievers, 'groups': [{'name': 'physics', 'start_at': 0}]}

    def get_general_rule2(self, institute_id):
        self.generate_referenced_question_retrievers(institute_id)
        self.generate_query_question_retrievers(institute_id)
        self.generate_exact_question_retrievers(institute_id)

        return {
            'retrievers': self.referenced_question_retrievers + self.query_question_retrievers + self.exact_question_retrievers,
            'groups': [{'name': 'physics', 'start_at': 0}, {'name': 'math', 'start_at': 1}]}

    def get_general_descriptive_rule(self, institute_id):
        self.generate_referenced_question_retrievers(institute_id, question_type='descriptive')
        self.generate_query_question_retrievers(institute_id, question_type='descriptive')
        self.generate_exact_question_retrievers(institute_id, question_type='descriptive')

        return {
            'retrievers': self.referenced_question_retrievers + self.query_question_retrievers + self.exact_question_retrievers,
            'groups': [{'name': 'physics', 'start_at': 0}]}

    def rule_and_generated_exam_validator(self, rule, generated_exam, error_message):
        self.tester.assertIsNotNone(generated_exam, error_message)
        self.tester.assertEqual(len(rule['groups']), len(generated_exam['groups']), error_message)
        self.tester.assertIn('questions_count', generated_exam['groups'][0], error_message)
        self.tester.assertIn('durations', generated_exam['groups'][0], error_message)
        self.tester.assertIn('positive_scores', generated_exam['groups'][0], error_message)
        self.tester.assertIn('negative_scores', generated_exam['groups'][0], error_message)

    def general_rule_blueprint_validator(self, blueprint, error_message):
        # TODO more tests
        self.tester.assertEqual(0, blueprint['groups'][0]['start_at'], error_message)
        self.tester.assertEqual('physics', blueprint['groups'][0]['name'], error_message)

    def __init__(self, tester):
        self.tester = tester

    name_index = 0

    def new_name(self):
        self.name_index += 1
        return 'new_name' + str(self.name_index)

    def quiz_response_validator(self, response_data, error_message, context_institute_id, expected_questions_count, public,
                                reanswerable, single_question_at_a_time, shuffle_questions, shuffle_choices, start, end, duration,
                                poster, quiz_id=None, remove_poster=None, rule=None, description=None):

        old_exam = None
        if quiz_id:
            (response_data2, status_code2, error_message2) = self.tester.post_with_token(self.get, {'id': quiz_id},
                                                                                         application_json=True)
            self.tester.assertEqual(200, status_code2, error_message2)
            self.tester.assertEqual('ok', response_data2['status'], error_message2)
            self.tester.assertEqual('Successful', response_data2['message'], error_message2)
            old_exam = response_data2['data']['quiz']

        self.tester.assertEqual('ok', response_data['status'], error_message)
        self.tester.assertEqual('Successful', response_data['message'], error_message)

        self.tester.assertIn('quiz', response_data['data'], error_message)
        self.tester.assertIn('questions_count', response_data['data']['quiz'], error_message)

        if expected_questions_count:
            self.tester.assertEqual(expected_questions_count, response_data['data']['quiz']['questions_count'], error_message)

        if reanswerable is not None:
            self.tester.assertIn('reanswerable', response_data['data']['quiz'], error_message)
            self.tester.assertEqual(reanswerable, response_data['data']['quiz']['reanswerable'], error_message)

            if reanswerable:
                self.tester.assertEqual(single_question_at_a_time, response_data['data']['quiz']['single_question_at_a_time'],
                                        error_message)
            else:
                self.tester.assertEqual(True, response_data['data']['quiz']['single_question_at_a_time'], error_message)
        else:
            if quiz_id:
                # old value
                self.tester.assertEqual(old_exam['reanswerable'], response_data['data']['quiz']['reanswerable'], error_message)
            else:
                # default behaviour
                pass
            pass

        # if single_question_at_a_time is not None:
        # if reanswerable:
        #     self.tester.assertEqual(single_question_at_a_time,
        #                             response_data['data']['quiz']['single_question_at_a_time'], error_message)
        # else:
        #     self.tester.assertEqual(True, response_data['data']['quiz']['single_question_at_a_time'],
        #                             error_message)
        # pass
        # else:
        # pass
        # if quiz_id:
        #     # old value
        #     pass
        # else:
        #     # default behaviour
        #     pass
        # pass

        if shuffle_questions is not None:
            self.tester.assertEqual(shuffle_questions, response_data['data']['quiz']['shuffle_questions'], error_message)
        else:
            if quiz_id:
                # old value
                self.tester.assertEqual(old_exam['shuffle_questions'], response_data['data']['quiz']['shuffle_questions'],
                                        error_message)
            else:
                # default behaviour
                pass
            pass

        if shuffle_choices is not None:
            self.tester.assertEqual(shuffle_choices, response_data['data']['quiz']['shuffle_choices'], error_message)
        else:
            if quiz_id:
                # old value
                self.tester.assertEqual(old_exam['shuffle_choices'], response_data['data']['quiz']['shuffle_choices'],
                                        error_message)
            else:
                # default behaviour
                pass
            pass

        if start is not None:
            self.tester.assertIn('start', response_data['data']['quiz'], error_message)
        else:
            if quiz_id:
                # old value
                self.tester.assertEqual(old_exam['start'], response_data['data']['quiz']['start'], error_message)
            else:
                self.tester.assertIsNone(response_data['data']['quiz']['start'], error_message)
            pass

        if end is not None:
            self.tester.assertIn('end', response_data['data']['quiz'], error_message)
        else:
            if quiz_id:
                # old value
                self.tester.assertEqual(old_exam['end'], response_data['data']['quiz']['end'], error_message)
            else:
                self.tester.assertIsNone(response_data['data']['quiz']['end'], error_message)

        if duration is not None:
            self.tester.assertIn('duration', response_data['data']['quiz'], error_message)
        else:
            if quiz_id:
                # old value
                self.tester.assertEqual(old_exam['duration'], response_data['data']['quiz']['duration'], error_message)
            else:
                self.tester.assertIsNone(response_data['data']['quiz']['duration'], error_message)

        if description is not None:
            self.tester.assertIn('description', response_data['data']['quiz'], error_message)
            self.tester.assertEqual(description, response_data['data']['quiz']['description'], error_message)
        else:
            if quiz_id:
                # old value
                self.tester.assertEqual(old_exam['description'], response_data['data']['quiz']['description'], error_message)
            else:
                self.tester.assertIsNone(response_data['data']['quiz']['description'], error_message)

        if remove_poster is not None:
            if quiz_id:
                self.tester.assertIsNone(response_data['data']['quiz']['poster'], error_message)
        else:
            # old value
            if poster is not None:
                # TODO
                # self.tester.assertEqual(override_institute_logo, response_data['data']['quiz']['poster'], error_message)
                pass
            else:
                if quiz_id:
                    # old value
                    self.tester.assertEqual(old_exam['poster'], response_data['data']['quiz']['poster'], error_message)
                else:
                    self.tester.assertIsNone(response_data['data']['quiz']['poster'], error_message)

        self.tester.assertIn('creator', response_data['data']['quiz'], error_message)
        self.tester.assertIn('last_editor', response_data['data']['quiz'], error_message)
        self.tester.assertIsNotNone(response_data['data']['quiz']['creator'], error_message)

        # TODO price

    # This function returns different types of responses based on validate_success
    def create_quiz_helper(self, context_institute_id, name=None, rule=None, expected_questions_count=None, public=None,
                           apply_negative_scores=None, reanswerable=None, shuffle_questions=None, shuffle_choices=None,
                           single_question_at_a_time=None, start=None, end=None, duration=None, poster=None,
                           validate_success=True, price=None, finish_message=None, theme=None, description=None):

        if name is None:
            name = self.tester.quizHelper.new_name()

        request = {'context_institute_id': context_institute_id, 'name': name, 'theme': 'dark', }

        if validate_success and rule is not None and expected_questions_count is None:
            self.tester.assertEqual(True, False, "when rule is not None, you better provide expected_questions_count too")

        using_general_rule = False
        if rule is None:
            rule = self.get_general_rule(context_institute_id)
            using_general_rule = True

        request['rule'] = json.dumps(rule)

        if expected_questions_count is None:
            expected_questions_count = 3

        if public:
            request['public'] = public

        if apply_negative_scores:
            request['apply_negative_scores'] = apply_negative_scores

        if theme:
            request['theme'] = theme

        if reanswerable:
            request['reanswerable'] = reanswerable

        if description:
            request['description'] = description

        if single_question_at_a_time:
            request['single_question_at_a_time'] = single_question_at_a_time

        if shuffle_questions:
            request['shuffle_questions'] = shuffle_questions

        if shuffle_choices:
            request['shuffle_choices'] = shuffle_choices

        if start:
            request['start'] = start

        if end:
            request['end'] = end

        if duration:
            request['duration'] = duration

        if finish_message:
            request['finish_message'] = finish_message

        if price:
            request['price'] = price

        if poster:
            request['poster'] = poster

        (response_data, status_code, error_message) = self.tester.post_with_token(self.create, request)

        if validate_success:
            self.quiz_response_validator(response_data, error_message, context_institute_id, expected_questions_count, public,
                                         reanswerable, single_question_at_a_time, shuffle_questions, shuffle_choices, start, end,
                                         duration, poster, description=description)

            return response_data['data']['quiz']['id']
        else:
            return response_data, status_code, error_message

    def edit_quiz_helper(self, institute_id, quiz_id, name=None, theme=None, rule=None, expected_questions_count=None,
                         public=False, reanswerable=True, shuffle_questions=None, shuffle_choices=None,
                         single_question_at_a_time=False, start=None, end=None, duration=None, poster=None, remove_poster=None,
                         validate_success=True, price=None, finish_message=None, description=None):

        if theme is None:
            theme = 'dark'

        request = {'context_institute_id': institute_id, 'quiz_id': quiz_id, 'theme': theme}

        if rule is not None:
            request['rule'] = json.dumps(rule)

        if name is not None:
            request['name'] = name

        if description is not None:
            request['description'] = description
        if public is not None:
            request['public'] = public

        if reanswerable is not None:
            request['reanswerable'] = reanswerable

        if finish_message is not None:
            request['finish_message'] = finish_message

        if price is not None:
            request['price'] = price

        if single_question_at_a_time is not None:
            request['single_question_at_a_time'] = single_question_at_a_time

        if shuffle_questions is not None:
            request['shuffle_questions'] = shuffle_questions

        if shuffle_choices is not None:
            request['shuffle_choices'] = shuffle_choices

        if start is not None:
            request['start'] = start.strftime(self.date_time_format)

        if end is not None:
            request['end'] = end.strftime(self.date_time_format)

        if duration is not None:
            request['duration'] = duration

        if poster is not None:
            request['poster'] = poster

        if remove_poster is not None:
            request['remove_poster'] = remove_poster

        (response_data, status_code, error_message) = self.tester.post_with_token(self.edit, request)

        if validate_success:
            self.quiz_response_validator(response_data, error_message, institute_id, expected_questions_count, public,
                                         reanswerable, single_question_at_a_time, shuffle_questions, shuffle_choices, start, end,
                                         duration, poster, remove_poster=remove_poster, quiz_id=quiz_id, description=description)

            return response_data['data']['quiz']['id']
        else:
            return response_data, status_code, error_message

    def archive_quiz_helper(self, context_institute_id, quiz_id, validate_success=True):

        request = {'context_institute_id': context_institute_id, 'quiz_id': quiz_id, }

        (response_data, status_code, error_message) = self.tester.post_with_token(self.archive, request)

        if validate_success:
            self.tester.assertEqual('ok', response_data['status'], error_message)
            self.tester.assertEqual('Successful', response_data['message'], error_message)
            return response_data, status_code, error_message
        else:
            return response_data, status_code, error_message

    def generate_quiz_sheet_for_institute_student_helper(self, institute_id, start=None, end=None, duration=None,
                                                         single_question_at_a_time=False, reanswerable=True,
                                                         shuffle_questions=None, shuffle_choices=None, rule=None,
                                                         expected_questions_count=None, quiz_name=None, quiz_id=None,
                                                         validate_success=True, public=None, apply_negative_scores=None):
        api_key = self.tester.instituteHelper.generate_api_key(institute_id)
        referer_institute_student_identity = "12345"
        wikiazma_institute_student_id = self.tester.instituteHelper.create_institute_student(api_key,
                                                                                             referer_institute_student_identity)

        if quiz_id is None:
            quiz_id = self.tester.quizHelper.create_quiz_helper(institute_id, shuffle_questions=shuffle_questions,
                                                                shuffle_choices=shuffle_choices, reanswerable=reanswerable,
                                                                start=start, end=end, apply_negative_scores=apply_negative_scores,
                                                                single_question_at_a_time=single_question_at_a_time, rule=rule,
                                                                expected_questions_count=expected_questions_count, name=quiz_name,
                                                                public=public)

        (response_data, status_code, error_message) = self.tester.post_without_token(self.generate_exam_page, {'api_key': api_key,
                                                                                                               'wikiazma_institute_student_id': wikiazma_institute_student_id,
                                                                                                               'wikiazma_quiz_id': quiz_id,
                                                                                                               'start': '' if start is None else start,
                                                                                                               'end': '' if end is None else end,
                                                                                                               'duration': '' if duration is None else duration,
                                                                                                               'shuffle_questions': '' if shuffle_questions is None else shuffle_questions,
                                                                                                               'shuffle_choices': '' if shuffle_choices is None else shuffle_choices,
                                                                                                               'public': '' if public is None else public, })

        if validate_success:
            self.tester.assertEqual(200, status_code, error_message)
            self.tester.assertEqual('ok', response_data['status'], error_message)
            self.tester.assertEqual('Successful', response_data['message'], error_message)
            self.tester.assertEqual(QUIZ_SHEET_STATE_NOT_ATTENDED, response_data['data']['quiz_sheet']['state'], error_message)
            self.tester.assertIsNone(response_data['data']['quiz_sheet']['final_score'], error_message)

            self.tester.assertIn('allowed_start_at', response_data['data']['quiz_sheet'], error_message)
            if start:
                self.tester.assertIsNotNone(response_data['data']['quiz_sheet']['allowed_start_at'],
                                            error_message)  # TODO assertEqual
            else:
                self.tester.assertIsNone(response_data['data']['quiz_sheet']['allowed_start_at'], error_message)

            using_general_rule = False
            if rule is None:
                rule = self.get_general_rule(institute_id)
                using_general_rule = True

            # public exams have generated_exam in their exam object
            # self.rule_and_generated_exam_validator(rule,
            #                                         response_data['data']['exam_page']['generated_exam'],
            #                                         error_message)
            # if using_general_rule:
            #     self.general_rule_generated_exam_validator(rule,
            #                                                response_data['data']['exam_page']['generated_exam'],
            #                                                error_message)

            # if start:
            #     self.tester.assertEqual(start, datetime.fromisoformat(
            #         response_data['data']['quiz_sheet']['start']), error_message)
            # else:
            #     self.tester.assertIsNone(
            #         response_data['data']['quiz_sheet']['start'], error_message)
            #
            # if end:
            #     self.tester.assertEqual(end, datetime.fromisoformat(
            #         response_data['data']['quiz_sheet']['end']), error_message)
            # else:
            #     self.tester.assertIsNone(
            #         response_data['data']['quiz_sheet']['end'], error_message)
            #
            # if duration:
            #     self.tester.assertEqual(
            #         duration, response_data['data']['quiz_sheet']['duration'], error_message)
            # else:
            #     self.tester.assertIsNone(
            #         response_data['data']['quiz_sheet']['duration'], error_message)

            self.tester.assertIn('referer_id', response_data['data']['quiz_sheet'], error_message)
            return response_data['data']['quiz_sheet']['referer_id'], response_data['data']['quiz_sheet']['entrance_token']
        else:
            return response_data, status_code, error_message

    def basic_enter_quiz_helper(self, quiz_id=None, quiz_sheet_id=None, entrance_token=None, force_send_token=None,
                                validate_success=True):
        request = {}

        send_token = False

        if quiz_id:
            request['quiz_id'] = quiz_id
            send_token = True

        if quiz_sheet_id:
            request['quiz_sheet_id'] = quiz_sheet_id
            send_token = True

        if entrance_token:
            request['entrance_token'] = entrance_token
            send_token = False

        if force_send_token is True or (force_send_token is None and send_token is True):
            (response_data, status_code, error_message) = self.tester.post_with_token(self.enter, request)
        else:
            (response_data, status_code, error_message) = self.tester.post_without_token(self.enter, request)

        if validate_success:
            self.tester.assertEqual(200, status_code, error_message)
            self.tester.assertEqual('ok', response_data['status'], error_message)
            self.tester.assertEqual('Successful', response_data['message'], error_message)

            return response_data['data']['quiz_sheet']['referer_id']
        else:
            return response_data, status_code, error_message

    def enter_exam_helper(self, context_institute_id, entrance_type, start=None, end=None, duration=None, reanswerable=True,
                          shuffle_questions=None, shuffle_choices=None, single_question_at_a_time=False, price=None,
                          apply_negative_scores=None, validate_success=True, rule=None, expected_questions_count=None,
                          send_token=None, quiz_name=None, quiz_id=None, finish_message=None):

        if quiz_id is not None and quiz_name is not None:
            print("warning #43284293")

        if entrance_type == ENTRANCE_TYPE_PUBLIC:
            if quiz_id is None:
                quiz_id = self.create_quiz_helper(context_institute_id, public=True, start=start, end=end, duration=duration,
                                                  reanswerable=reanswerable, shuffle_questions=shuffle_questions,
                                                  shuffle_choices=shuffle_choices,
                                                  single_question_at_a_time=single_question_at_a_time, rule=rule,
                                                  apply_negative_scores=apply_negative_scores,
                                                  expected_questions_count=expected_questions_count, price=price,
                                                  finish_message=finish_message, name=quiz_name)

            if send_token is False:
                (response_data, status_code, error_message) = self.tester.post_without_token(self.enter, {'quiz_id': quiz_id, })
            else:
                (response_data, status_code, error_message) = self.tester.post_with_token(self.enter, {'quiz_id': quiz_id, })

            if validate_success:
                self.tester.assertEqual(200, status_code, error_message)
                self.tester.assertEqual('ok', response_data['status'], error_message)
                quiz_sheet_id = response_data['data']['quiz_sheet']['referer_id']

        elif entrance_type == ENTRANCE_TYPE_API_INVITE:
            quiz_sheet_id, entrance_token = self.generate_quiz_sheet_for_institute_student_helper(context_institute_id,
                                                                                                  start=start, end=end,
                                                                                                  duration=duration,
                                                                                                  reanswerable=reanswerable,
                                                                                                  single_question_at_a_time=single_question_at_a_time,
                                                                                                  shuffle_questions=shuffle_questions,
                                                                                                  shuffle_choices=shuffle_choices,
                                                                                                  validate_success=True,
                                                                                                  rule=rule,
                                                                                                  apply_negative_scores=apply_negative_scores,
                                                                                                  quiz_name=quiz_name,
                                                                                                  quiz_id=quiz_id,
                                                                                                  expected_questions_count=expected_questions_count, )
            # attending an exam in the start and end range
            (response_data, status_code, error_message) = self.tester.post_without_token(self.enter,
                                                                                         {'entrance_token': entrance_token, })
        else:
            # TODO
            return None, None

        if validate_success:
            self.tester.assertEqual(200, status_code, error_message)
            self.tester.assertEqual('ok', response_data['status'], error_message)
            self.tester.assertEqual('Successful', response_data['message'], error_message)
            self.tester.assertIsNotNone(response_data['data']['current_date_time'], error_message)

            self.tester.assertEqual(QUIZ_SHEET_STATE_IN_PROGRESS, QuizSheet.objects.get(referer_id=quiz_sheet_id).state,
                                    "expected the status to be IN_PROGRESS")

            if entrance_type == ENTRANCE_TYPE_PUBLIC:
                return quiz_sheet_id, None
            elif entrance_type == ENTRANCE_TYPE_API_INVITE:
                return quiz_sheet_id, entrance_token
        else:
            return response_data, status_code, error_message

    def get_ready_an_exam_page_with_answers(self, institute_id, entrance_type, entrance_token=None, public_id=None, start=None,
                                            end=None, duration=None, reanswerable=True, apply_negative_scores=None,
                                            shuffle_questions=None, shuffle_choices=None, single_question_at_a_time=False,
                                            exam_name=None, validate_success=True, quiz_id=None, selected_choices=None):
        (quiz_sheet_id, entrance_token) = self.enter_exam_helper(institute_id, entrance_type=entrance_type,
                                                                 start=start, end=end, duration=duration,
                                                                 reanswerable=reanswerable,
                                                                 apply_negative_scores=apply_negative_scores,
                                                                 shuffle_questions=shuffle_questions,
                                                                 shuffle_choices=shuffle_choices,
                                                                 quiz_id=quiz_id,
                                                                 single_question_at_a_time=single_question_at_a_time,
                                                                 validate_success=True, quiz_name=exam_name)
        if reanswerable:
            # set all three answers together
            request = {'user_inputs': json.dumps(  # Comment id #68246256; the answer to 3 first questions
                [{'index': 0, 'selected_choices': selected_choices if selected_choices else ['12345674', '12345675']},
                 {'index': 1, 'selected_choices': selected_choices if selected_choices else ['12345674', '12345675']},
                 {'index': 2, 'selected_choices': selected_choices if selected_choices else ['12345674', '12345675']}])}
            if entrance_token:
                request['entrance_token'] = entrance_token
                (response_data, status_code, error_message) = self.tester.post_without_token(self.set_answer, request)
            else:
                request['quiz_sheet_id'] = quiz_sheet_id
                (response_data, status_code, error_message) = self.tester.post_with_token(self.set_answer, request)
            if validate_success:
                self.tester.assertEqual(200, status_code, error_message)
                self.tester.assertEqual('ok', response_data['status'], error_message)
                self.tester.assertEqual('Successful', response_data['message'], error_message)

                self.tester.assertEqual(3, len(QuizSheet.objects.get(referer_id=quiz_sheet_id)._quizee_inputs_sheet))

                return quiz_sheet_id, entrance_token
            else:
                return response_data, status_code, error_message
        else:
            response_data = None
            status_code = None
            error_message = None
            for i in range(0, 3):
                request = {'user_inputs': json.dumps(
                    [{'index': i, 'selected_choices': selected_choices if selected_choices else ['12345674', '12345675']}])}
                if entrance_token:
                    request['entrance_token'] = entrance_token
                    (response_data, status_code, error_message) = self.tester.post_without_token(self.set_answer, request)
                else:
                    request['quiz_sheet_id'] = quiz_sheet_id
                    (response_data, status_code, error_message) = self.tester.post_with_token(self.set_answer, request)

                if validate_success:
                    self.tester.assertEqual(200, status_code, error_message)
                    self.tester.assertEqual('ok', response_data['status'], error_message)
                    self.tester.assertEqual('Successful', response_data['message'], error_message)

                    self.tester.assertEqual(i + 1, len(QuizSheet.objects.get(referer_id=quiz_sheet_id)._quizee_inputs_sheet))

            if validate_success:
                return quiz_sheet_id, entrance_token
            else:
                return response_data, status_code, error_message

    def finish_a_quiz_sheet_with_answers(self, institute_id, entrance_type, start=None, end=None, duration=None,
                                         reanswerable=True, shuffle_questions=None, shuffle_choices=None, quiz_id=None,
                                         single_question_at_a_time=False, exam_name=None, apply_negative_scores=None,
                                         validate_success=True, selected_choices=None):
        if entrance_type == ENTRANCE_TYPE_API_INVITE:
            pass
        (quiz_sheet_id, entrance_token) = self.get_ready_an_exam_page_with_answers(institute_id, entrance_type=entrance_type,
                                                                                   start=start, end=end, duration=duration,
                                                                                   reanswerable=reanswerable,
                                                                                   apply_negative_scores=apply_negative_scores,
                                                                                   shuffle_questions=shuffle_questions,
                                                                                   shuffle_choices=shuffle_choices,
                                                                                   quiz_id=quiz_id,
                                                                                   selected_choices=selected_choices,
                                                                                   single_question_at_a_time=single_question_at_a_time,
                                                                                   exam_name=exam_name, validate_success=True)

        request = {}
        if entrance_token:
            request['entrance_token'] = entrance_token
            (response_data, status_code, error_message) = self.tester.post_without_token(self.finish, request)
        else:
            request['quiz_sheet_id'] = quiz_sheet_id
            (response_data, status_code, error_message) = self.tester.post_with_token(self.finish, request)

        if validate_success:
            self.tester.assertEqual(200, status_code, error_message)
            self.tester.assertEqual('ok', response_data['status'], error_message)
            self.tester.assertEqual('Successful', response_data['message'], error_message)

            self.tester.assertIn('report_card', response_data['data'], error_message)
            self.tester.assertIn('final_score', response_data['data'], error_message)
            self.tester.assertIn('max_positive_score', response_data['data'], error_message)

            self.tester.assertEqual(7.5, response_data['data']['max_positive_score'], error_message)
            self.tester.assertEqual(7.5, response_data['data']['report_card']['overall']['positive_scores'], error_message)
            self.tester.assertEqual(3.0, response_data['data']['report_card']['overall']['negative_scores'], error_message)
            self.tester.assertEqual(3, response_data['data']['report_card']['overall']['questions_count'], error_message)

            if not shuffle_choices and not shuffle_questions:
                self.tester.assertNotEqual(0, QuestionScore.objects.filter(quiz_sheet_id=quiz_sheet_id).count(),
                                           error_message)

                # For manually calculating scores, checkout Comment id #68246256
                if apply_negative_scores:
                    self.tester.assertEqual(-3.0, response_data['data']['final_score'], error_message)
                    self.tester.assertEqual(-3.0, response_data['data']['report_card']['overall']['score'],
                                            error_message)
                else:
                    self.tester.assertEqual(7.5, response_data['data']['final_score'], error_message)
                    self.tester.assertEqual(7.5, response_data['data']['report_card']['overall']['score'],
                                            error_message)
            #
            # if shuffle_choices:
            #     self.tester.assertNotEqual(0, Corrections.objects.filter(exam_page_id=exam_page_id).count(),
            #                                error_message)
            #     self.tester.assertGreaterEqual(
            #         7.5, response_data['data']['final_score'], error_message)
            #
            # if shuffle_questions:
            #     self.tester.assertNotEqual(0, Corrections.objects.filter(exam_page_id=exam_page_id).count(),
            #                                error_message)
            #     self.tester.assertGreaterEqual(
            #         7.5, response_data['data']['final_score'], error_message)

            return quiz_sheet_id, entrance_token

        else:
            return response_data, status_code, error_message

    def set_answer_helper(self, user_inputs, quiz_sheet_id=None, entrance_token=None, should_return_not_found_error=False,
                          error_400=False, error_401=False, error_404=False, message='You should send only one answer at a time',
                          status='error'):
        request = {}
        if user_inputs:
            request['user_inputs'] = user_inputs
        if entrance_token:
            request['entrance_token'] = entrance_token
        else:
            request['quiz_sheet_id'] = quiz_sheet_id
        if entrance_token is not None:
            (response_data, status_code, error_message) = self.tester.post_without_token(self.set_answer, request)
        else:
            (response_data, status_code, error_message) = self.tester.post_with_token(self.set_answer, request)

        if should_return_not_found_error:
            self.tester.assertEqual(404, status_code, error_message)
            self.tester.assertEqual('not_found', response_data['status'], error_message)

        elif error_401:
            self.tester.assertEqual(401, status_code, error_message)
            self.tester.assertEqual("error", response_data['status'], error_message)
            self.tester.assertEqual('Access denied', response_data['message'], error_message)

        elif error_400:
            self.tester.assertEqual(400, status_code, error_message)
            self.tester.assertEqual(status, response_data['status'], error_message)
            if message:
                self.tester.assertEqual(message, response_data['message'], error_message)
        elif error_404:
            self.tester.assertEqual(404, status_code, error_message)
            self.tester.assertEqual(status, response_data['status'], error_message)
            if message:
                self.tester.assertIn('message', response_data, error_message)
                self.tester.assertEqual(message, response_data['message'], error_message)
        else:
            self.tester.assertEqual(200, status_code, error_message)
            self.tester.assertEqual('ok', response_data['status'], error_message)
            self.tester.assertEqual('Successful', response_data['message'], error_message)

        return response_data, status_code, error_message

    def get_exam_cover_helper(self, quiz_sheet_id=None, quiz_id=None, entrance_token=None):
        request = {}
        if entrance_token is not None:
            request['entrance_token'] = entrance_token
            (response_data, status_code, error_message) = self.tester.post_without_token(self.gateway, request)
        elif quiz_sheet_id is not None:
            request['quiz_sheet_id'] = quiz_sheet_id
            (response_data, status_code, error_message) = self.tester.post_with_token(self.gateway, request)
        else:
            request['quiz_id'] = quiz_id
            (response_data, status_code, error_message) = self.tester.post_with_token(self.gateway, request)

        self.tester.assertEqual(200, status_code, error_message)
        self.tester.assertEqual('ok', response_data['status'], error_message)
        self.tester.assertIn('current_date_time', response_data['data'], error_message)

        self.tester.assertIn('institute', response_data['data'], error_message)

        self.tester.assertIn('info', response_data['data'], error_message)
        self.tester.assertIn('poster', response_data['data']['info'], error_message)
        self.tester.assertIn('poster_thumbnail', response_data['data']['info'], error_message)
        self.tester.assertIn('name', response_data['data']['info'], error_message)
        self.tester.assertIn('description', response_data['data']['info'], error_message)

        self.tester.assertIn('price', response_data['data']['info'], error_message)
        self.tester.assertIn('start', response_data['data']['info'], error_message)
        self.tester.assertIn('end', response_data['data']['info'], error_message)
        self.tester.assertIn('duration', response_data['data']['info'], error_message)

        # quiz_sheet tests  # if device_token is not None:  #     self.tester.assertIsNotNone(  #         response_data['data']['started_at']['info'], error_message)  # else:  #     self.tester.assertIsNone(  #         response_data['data']['started_at']['info'], error_message)  # self.tester.assertIn('state', response_data['data']['info'], error_message)  # self.tester.assertNotIn(  #     'final_score', response_data['data'], error_message)  # self.tester.assertNotIn(  #     'generated_exam', response_data['data'], error_message)  # self.tester.assertNotIn(  #     'user_inputs', response_data['data'], error_message)  # self.tester.assertNotIn(  #     'corrections', response_data['data'], error_message)  # if quiz_id is not None:  #     if device_token is not None:  #         self.tester.assertEqual(  #             'in_progress', response_data['data']['state'], error_message)  #     else:  #         self.tester.assertEqual(  #             'not_generated', response_data['data']['state'], error_message)  # else:  #     if device_token is not None:  #         self.tester.assertEqual(  #             'in_progress', response_data['data']['state'], error_message)  #     else:  #         self.tester.assertEqual(  #             'not_attended', response_data['data']['state'], error_message)

    def generate_invoice_helper(self, quiz_id, expected_total_cost=None):
        request = {}
        if quiz_id:
            request['quiz_id'] = quiz_id
            (response_data, status_code, error_message) = self.tester.post_with_token(self.invoice, request)
            self.tester.assertEqual(200, status_code, error_message)
            self.tester.assertEqual('ok', response_data['status'], error_message)
            self.tester.assertEqual('Successful', response_data['message'], error_message)
            self.tester.assertIn('invoice', response_data['data'], error_message)
            self.tester.assertIn('balance', response_data['data'], error_message)
            self.tester.assertIsNotNone(response_data['data']['balance'], error_message)
            self.tester.assertLessEqual(0, response_data['data']['balance'], error_message)

            if expected_total_cost:
                self.tester.assertEqual(expected_total_cost, response_data['data']['invoice']['total_cost'], error_message)

    def answer_file_helper(self, device_token, group_index, question_index, file, public_id=None, entrance_token=None,
                           error_400=False, error_409=False):
        request = {'device_token': device_token, 'group_index': group_index, 'question_index': question_index, 'file': file}
        if entrance_token is not None:
            request['entrance_token'] = entrance_token
            (response_data, status_code, error_message) = self.tester.post_without_token(
                self.institute_student_exam_page_answer_file_add if entrance_token is not None else self.public_exam_page_answer_file_add,
                request)
        else:
            request['public_id'] = public_id
            (response_data, status_code, error_message) = self.tester.post_with_token(
                self.institute_student_exam_page_answer_file_add if entrance_token is not None else self.public_exam_page_answer_file_add,
                request)

        if error_400:
            self.tester.assertEqual('error', response_data['status'], error_message)
            self.tester.assertIn('you can not attach file to this question', response_data['message'], error_message)
        elif error_409:
            self.tester.assertEqual(409, status_code, error_message)
            self.tester.assertEqual('index_error', response_data['status'], error_message)
        else:
            self.tester.assertEqual(200, status_code, error_message)
            self.tester.assertEqual('ok', response_data['status'], error_message)
            self.tester.assertEqual('Successful', response_data['message'], error_message)
        return response_data, status_code, error_message

    def delete_answer_file_helper(self, file_id, device_token, group_index, question_index, public_id=None, entrance_token=None,
                                  error_404=False):
        request = {'file_id': file_id, 'device_token': device_token, 'group_index': group_index, 'question_index': question_index}

        if entrance_token is not None:
            request['entrance_token'] = entrance_token
        else:
            request['public_id'] = public_id

        (response_data, status_code, error_message) = self.tester.post_without_token(
            self.institute_student_exam_page_answer_file_delete if entrance_token is not None else self.public_exam_page_answer_file_delete,
            request)
        if error_404:
            self.tester.assertEqual(404, status_code, error_message)
            self.tester.assertEqual('not_found', response_data['status'], error_message)
            self.tester.assertEqual('File not found', response_data['message'], error_message)
        else:
            self.tester.assertEqual(200, status_code, error_message)
            self.tester.assertEqual('ok', response_data['status'], error_message)
            self.tester.assertEqual('Successful', response_data['message'], error_message)
            return response_data, status_code, error_message

    def pause_exam_helper(self, quiz_id, pause):
        request = {'quiz_id': quiz_id, 'pause': pause}

        if pause is not None:
            request['pause'] = pause

        (response_data, status_code, error_message) = self.tester.post_with_token(self.pause_exam, request)

        self.tester.assertEqual(200, status_code, error_message)
        self.tester.assertEqual('ok', response_data['status'], error_message)
        self.tester.assertEqual('Successful', response_data['message'], error_message)

    def extend_exam_page_helper(self, quiz_id, start, end, duration):
        request = {'quiz_id': quiz_id}

        if start is not None:
            request['start'] = start

        if end is not None:
            request['end'] = end

        if duration is not None:
            request['duration'] = duration

        (response_data, status_code, error_message) = self.tester.post_with_token(self.extend_exam, request)

        return response_data, status_code, error_message

    def taken_exam_page_list_helper(self, skip, take):
        request = {'skip': skip, 'take': take}

        (response_data, status_code, error_message) = self.tester.post_with_token(self.taken_exam_page_list, request)

        return response_data, status_code, error_message

    def edit_exam_template_helper(self, institute_id, exam_template_id, name=None, rule=None, single_question_at_a_time=None,
                                  reanswerable=None, shuffle_questions=None, shuffle_choices=None, apply_negative_scores=None,
                                  examiner_info_visibility=None, theme=None, description=None, duration=None, price=None,
                                  finish_message=None, poster=None, remove_poster=False):
        request = {'context_institute_id': institute_id, 'quiz_template_id': exam_template_id}

        if name is not None:
            request['name'] = name

        if rule is not None:
            request['rule'] = rule

        if poster is not None:
            request['poster'] = poster

        if single_question_at_a_time is not None:
            request['single_question_at_a_time'] = single_question_at_a_time

        if remove_poster is not None:
            request['remove_poster'] = remove_poster

        if reanswerable is not None:
            request['reanswerable'] = reanswerable

        if shuffle_questions is not None:
            request['shuffle_questions'] = shuffle_questions

        if shuffle_choices is not None:
            request['shuffle_choices'] = shuffle_choices

        if apply_negative_scores is not None:
            request['apply_negative_scores'] = apply_negative_scores

        if theme is not None:
            request['theme'] = theme

        if description is not None:
            request['description'] = description

        if duration is not None:
            request['duration'] = duration

        if price is not None:
            request['price'] = price

        if finish_message is not None:
            request['finish_message'] = finish_message

        if examiner_info_visibility is not None:
            request['examiner_info_visibility'] = examiner_info_visibility

        (response_data, status_code, error_message) = self.tester.post_with_token(self.edit_quiz_template, request)

        return response_data, status_code, error_message

    def get_exam_page_helper(self, exam_page_id):
        request = {'exam_page_id': exam_page_id}

        (response_data, status_code, error_message) = self.tester.post_with_token(self.exam_page_get, request)

        return response_data, status_code, error_message

    def correction_add_helper(self, quiz_sheet_id, index, score, comment_for_student=None, comment_for_other_correctors=None):
        request = {'quiz_sheet_id': quiz_sheet_id, 'index': index, 'score': score}

        if comment_for_other_correctors:
            request['comment_for_other_correctors'] = comment_for_other_correctors

        if comment_for_student:
            request['comment_for_student'] = comment_for_student

        (response_data, status_code, error_message) = self.tester.post_with_token(self.quiz_sheet_correction_add, request)

        return response_data, status_code, error_message

    def exam_page_correction_publish_helper(self, quiz_sheet_id):
        request = {'quiz_sheet_id': quiz_sheet_id}

        (response_data, status_code, error_message) = self.tester.post_with_token(self.exam_page_correction_publish, request)

        return response_data, status_code, error_message

    def institute_student_finish_quiz_sheet_helper(self, institute_id, start=None, end=None, duration=None, reanswerable=True,
                                                   shuffle_questions=None, shuffle_choices=None, single_question_at_a_time=False,
                                                   apply_negative_scores=None):
        (quiz_sheet_id, entrance_token) = self.get_ready_an_exam_page_with_answers(institute_id,
                                                                                   entrance_type=ENTRANCE_TYPE_API_INVITE,
                                                                                   start=start, end=end, duration=duration,
                                                                                   reanswerable=reanswerable,
                                                                                   apply_negative_scores=apply_negative_scores,
                                                                                   shuffle_questions=shuffle_questions,
                                                                                   shuffle_choices=shuffle_choices,
                                                                                   single_question_at_a_time=single_question_at_a_time,
                                                                                   validate_success=True)

        (response_data, status_code, error_message) = self.tester.post_without_token(self.finish,
                                                                                     {'entrance_token': entrance_token, })

        return response_data, status_code, error_message

    def get_quiz_sheet_helper(self, quiz_sheet_id):
        request = {'id': quiz_sheet_id}

        (response_data, status_code, error_message) = self.tester.post_with_token(self.get, request)

        return response_data, status_code, error_message
