class ExerciseQuestionsController < ApplicationController
  before_action :require_login, :check_auth #用户需登陆
  before_action :get_exercise, only: [:new, :create] #获取试卷
  before_action :get_exercise_question, except: [:new, :create] #获取试卷的问题及试卷
  before_action :is_course_teacher #是否为老师
  before_action :validate_params, only: [:create, :update] #传入参数的验证
  before_action :check_exercise_status, only: [:new, :create, :delete_answer, :destroy] #除未发布状态之外，其余状态不能进行增删操作
  before_action :cannot_change_column, only: [:update] #更新时不能更改的内容
  before_action :check_before_adjust_score, only: [:adjust_score]
  before_action :check_before_batch_adjust_score, only: [:batch_adjust_score]
  before_action :check_before_common_adjust_score, only: [:adjust_score, :batch_adjust_score]
  before_action :check_exercise_users, only: [:batch_adjust_score]
  include ExercisesHelper

  def new
    ActiveRecord::Base.transaction do
      begin
        @exercise_question = @exercise.exercise_questions.new
      rescue Exception => e
        uid_logger_error(e.message)
        tip_exception("页面访问失败失败！")
        raise ActiveRecord::Rollback
      end
    end
  end

  #question_type 0为单选题，1为多选题，2为判断题，3为填空题（单空和多空），4为简答题，5为实训题
  def create
    ActiveRecord::Base.transaction do
      question_options = {
        :question_title => params[:question_title],
        :question_type => params[:question_type].present? ? params[:question_type].to_i : 0, #默认为单选题
        :question_number => @exercise.exercise_questions.count + 1,
        :question_score => params[:question_score].present? ? params[:question_score].to_f.round(1) : 5.0,
        :shixun_id => params[:shixun_id].blank? ? "" : params[:shixun_id],
        :is_ordered => params[:is_ordered] # 填空题的答案是否为一一对应关系,默认为true即为一一对应
      }
      @exercise_question = @exercise.exercise_questions.new(question_options)
      #插入问题时，那么从插入的这个id以后的question_num都将要+1
      if params[:insert_id].present?
        insert_exercise = @exercise.exercise_questions.find_by(id: params[:insert_id])
        if insert_exercise.present? #如果该问题存在的话，意思是如果是第一题，那么就不存在插入
          ques_num = insert_exercise.question_number.to_i
          @exercise_question.question_number = ques_num + 1 #更新了问题的位置
          @exercise.exercise_questions.insert_question_ex(ques_num).update_all("question_number = question_number + 1")
        end
      end

      if @exercise_question.save!
        #为选择题(包括单选和多选)的时候,创建问题选项
        ques_type = @exercise_question.question_type
        if ques_type <= Exercise::MULTIPLE
          choices_array = params[:question_choices]
          choices_count = choices_array.count
          (1..choices_count).each do |c|
            choice = choices_array[c - 1] #每一个选项的内容
            choice_option = {
              :choice_position => c,
              :choice_text => choice
            }
            question_choices = @exercise_question.exercise_choices.new(choice_option)
            question_choices.save!
          end
          standard_answer = params[:standard_answers] #为数组格式，因为可能会有单选和多选，标准答案，已提前判断不能为空,
          standard_answer = standard_answer.uniq.reject(&:blank?)
          #标准答案的存储，如：["1","2","3"..]等，1对应A，2对应B，3对应C。。。
          standard_answer.each do |a|
            choice_id = a.to_i
            standard_option = {
              :exercise_question_id => @exercise_question.id,
              :exercise_choice_id => choice_id #即为选择的位置参数
            }
            question_standard_answer = ExerciseStandardAnswer.new(standard_option)
            question_standard_answer.save!
            if standard_answer.count > 1 && ques_type == Exercise::SINGLE #当标准答案数大于1，且不为多选时，修改为多选
              @exercise_question.update_attribute("question_type", Exercise::MULTIPLE)
            elsif standard_answer.count == 1 && ques_type == Exercise::MULTIPLE
              @exercise_question.update_attribute("question_type", Exercise::SINGLE)
            end
          end
        elsif ques_type == Exercise::JUDGMENT #这个为判断题
          choices_array = params[:question_choices] #判断的选项，对/错等等
          choices_count = choices_array.count
          (1..choices_count).each do |c|
            choice = choices_array[c - 1] #每一个选项的内容
            choice_option = {
              :choice_position => c,
              :choice_text => choice
            }
            question_choices = @exercise_question.exercise_choices.create!(choice_option)
            question_choices.save!
          end
          standard_answer = params[:standard_answers] #对应选项的id
          standard_option = {
            :exercise_question_id => @exercise_question.id,
            :exercise_choice_id => standard_answer.first.to_i
          }
          question_standard_answer = ExerciseStandardAnswer.new(standard_option)
          question_standard_answer.save!
        elsif ques_type == Exercise::COMPLETION #填空题，每空的参考答案有多个，那么以位置对应
          standard_answer = params[:standard_answers]
          standard_answer.each do |a|
            null_choice_id = a[:choice_id]
            null_choice_text = a[:answer_text]
            null_choice_text.each do |n|
              standard_option = {
                :exercise_question_id => @exercise_question.id,
                :exercise_choice_id => null_choice_id,
                :answer_text => n
              }
              question_standard_answer = ExerciseStandardAnswer.new(standard_option)
              question_standard_answer.save!
            end
          end
        elsif ques_type == Exercise::SUBJECTIVE #简答题
          if params[:standard_answers].present? && params[:standard_answers].reject(&:blank?).count > 0
            standard_answer = params[:standard_answers]
            standard_answer.each do |a|
              standard_option = {
                :exercise_question_id => @exercise_question.id,
                :answer_text => a,
              }
              question_standard_answer = ExerciseStandardAnswer.new(standard_option)
              question_standard_answer.save!
            end
          end
        elsif ques_type == Exercise::PRACTICAL #实训题
          shixun = Shixun.find_by(id: params[:shixun_id])
          shixun_scores = params[:question_scores] #试卷有多个的分值有多个分数表，所以为分数的数组
          shixun_name = params[:shixun_name] || shixun.name
          question_score = 0
          shixun.challenges.try(:each_with_index) do |challenge, index|
            shixun_option = {
              :challenge_id => challenge.id,
              :shixun_id => shixun.id,
              :exercise_question_id => @exercise_question.id,
              :position => (index + 1),
              :question_score => shixun_scores[index].present? ? shixun_scores[index].to_f.round(1) : 5
            }
            ex_shixun_challenge = ExerciseShixunChallenge.create!(shixun_option)
            question_score += ex_shixun_challenge.question_score # 问题的分数，为各个关卡分数的总和
          end
          @exercise_question.update!(:question_score => question_score, :shixun_name => shixun_name)
        end
      end
    end
  end

  def show
    ActiveRecord::Base.transaction do
      begin
        @exercise_choices = @exercise_question.exercise_choices
        @exercise_question_shixun = @exercise_question.exercise_shixun_challenges
      rescue Exception => e
        uid_logger_error(e.message)
        tip_exception("页面调用失败！")
        raise ActiveRecord::Rollback
      end
    end
  end

  def edit
    ActiveRecord::Base.transaction do
      @exercise_choices = @exercise_question.exercise_choices
      @exercise_question_shixun = @exercise_question.exercise_shixun_challenges
    end
  end

  def update
    ActiveRecord::Base.transaction do
      standard_answer_change = false
      # 更新试卷题目的内容
      question_options = {
        :question_title => params[:question_title],
        :is_ordered => params[:is_ordered], # 填空题的答案是否为一一对应关系,默认为true即为一一对应
        :question_score => params[:question_score].present? ? params[:question_score].to_f.round(1) : 5.0 #不可修改分数
      }
      choices_array = params[:question_choices]
      stan_answer_params = params[:standard_answers]
      standard_answer = stan_answer_params.present? ? stan_answer_params.uniq.reject(&:blank?) : []
      @exercise_question.update!(question_options)
      #当选项存在时，可修改选项内容，但是不能更改选项的位置（即不能增删选项）
      if choices_array.present?
        ex_choices = @exercise_question.exercise_choices
        ex_choice_count = ex_choices.count
        choice_array_count = choices_array.count
        ex_choice_count_array = (1..ex_choice_count).to_a
        choice_array_count_array = (1..choice_array_count).to_a
        if ex_choice_count > choice_array_count #如果选项有减少的，那么只更新传入的，删除以前的
          choice_array_count_array.each do |c|
            choice = choices_array[c - 1] #每一个选项的内容
            exercise_choice = @exercise_question.exercise_choices.find_choice_custom("choice_position", (c))
            exercise_choice.update(choice_text: choice)
          end
          drop_ex_choice = @exercise_question.exercise_choices.left_choice_choose("choice_position", (choice_array_count))
          drop_ex_choice.destroy_all
        else
          ex_choice_count_array.each do |c|
            choice = choices_array[c - 1] #每一个选项的内容
            exercise_choice = @exercise_question.exercise_choices.find_choice_custom("choice_position", (c))
            exercise_choice.update(choice_text: choice)
          end
          new_add_choice = choice_array_count_array - ex_choice_count_array #新传入的需新增
          if new_add_choice.count > 0
            new_add_choice.each do |i|
              choice_option = {
                :choice_position => i,
                :choice_text => choices_array[i - 1]
              }
              question_choices = @exercise_question.exercise_choices.new(choice_option)
              question_choices.save!
            end
          end
        end
      end
      #试卷未发布时，当标准答案存在时，可修改标准答案内容，可增删标准答案，否则只能修改标准答案，不能增删标准答案
      @exercise_answers_array = @exercise_question.exercise_standard_answers #问卷的全部标准答案
      if standard_answer.present?
        if @exercise_question.question_type <= Exercise::JUDGMENT #选择题/判断题，标准答案为一个或多个
          exercise_standard_choices = @exercise_answers_array.pluck(:exercise_choice_id) #问题以前的全部标准答案选项位置
          if exercise_standard_choices.sort != standard_answer.sort #表示答案有更改的
            standard_answer_change = true
            common_standard_choices = standard_answer & exercise_standard_choices # 传入的标准答案的选项位置和以前的并集，即表示不用做更改的
            old_left_standard_choices = exercise_standard_choices - common_standard_choices # 以前的差集共同的，剩余的表示需要删掉
            new_left_standard_choices = standard_answer - common_standard_choices # 传入的标准答案差集共同的，剩余的表示需要新建
            if old_left_standard_choices.count > 0
              @exercise_answers_array.standard_by_ids(old_left_standard_choices).destroy_all
            end
            if new_left_standard_choices.count > 0 #新建标准答案
              new_left_standard_choices.each do |s|
                standard_option = {
                  :exercise_question_id => @exercise_question.id,
                  :exercise_choice_id => s.to_i #即为选择的位置参数
                }
                question_standard_answer = ExerciseStandardAnswer.new(standard_option)
                question_standard_answer.save!
              end

            end
            if standard_answer.count > 1 && @exercise_question.question_type == Exercise::SINGLE #当标准答案数大于1，且不为多选时，修改为多选
              @exercise_question.update_attribute("question_type", Exercise::MULTIPLE)
            elsif standard_answer.count == 1 && @exercise_question.question_type == Exercise::MULTIPLE
              @exercise_question.update_attribute("question_type", Exercise::SINGLE)
            end
          end
        elsif @exercise_question.question_type == Exercise::COMPLETION #填空题
          old_ex_answer = @exercise_question.exercise_standard_answers #当前问题的全部标准答案
          old_ex_answer_choice_texts = old_ex_answer.pluck(:answer_text).sort
          new_ex_answer_choice_texts = standard_answer.pluck(:answer_text).sum.sort
          if old_ex_answer_choice_texts != new_ex_answer_choice_texts #填空题标准答案有更改时，才会更新标准答案
            new_ex_answer_choice_ids = standard_answer.map {|a| a[:choice_id]}.uniq #新传入的答案数组序号
            old_ex_answer_choice_ids = old_ex_answer.pluck(:exercise_choice_id).uniq #全部的答案数组序号
            standard_answer_change = true
            #删除多余的选项
            if old_ex_answer_choice_ids.count > new_ex_answer_choice_ids.count #有减少的填空
              delete_ex_answer_choice_ids = old_ex_answer_choice_ids - new_ex_answer_choice_ids
              old_ex_answer.standard_by_ids(delete_ex_answer_choice_ids).destroy_all
            end
            standard_answer.each do |aa|
              null_choice_id = aa[:choice_id]
              null_choice_text = aa[:answer_text]
              null_choice_text_count = null_choice_text.count #当前传入的答案数量
              null_choice_text_count_array = (1..null_choice_text_count).to_a

              ex_answer_pre = old_ex_answer.standard_by_ids(null_choice_id) #当前问题的全部答案
              ex_answer_pre_count = ex_answer_pre.count
              ex_answer_pre_count_array = (1..ex_answer_pre_count).to_a

              if old_ex_answer_choice_ids.include?(null_choice_id) #以前的填空题答案包含有现在的填空序号
                if null_choice_text_count >= ex_answer_pre_count
                  new_add_choice = null_choice_text_count_array - ex_answer_pre_count_array
                  ex_answer_pre_count_array.each do |n|
                    @hash_symbol_null_ = {
                      :exercise_question_id => @exercise_question.id,
                      :exercise_choice_id => null_choice_id,
                      :answer_text => null_choice_text[n - 1]
                    }
                    standard_option = @hash_symbol_null_
                    ex_answer_pre[n - 1].update(standard_option)
                  end
                  if new_add_choice.count > 0 #表示有新增的
                    new_add_choice.each do |i|
                      standard_option = {
                        :exercise_question_id => @exercise_question.id,
                        :exercise_choice_id => null_choice_id,
                        :answer_text => null_choice_text[i - 1]
                      }
                      question_standard_answer = ExerciseStandardAnswer.new(standard_option)
                      question_standard_answer.save!
                    end
                  end
                else
                  new_delete_choice = ex_answer_pre_count_array - null_choice_text_count_array
                  null_choice_text.each_with_index do |n, index|
                    standard_option = {
                      :exercise_question_id => @exercise_question.id,
                      :exercise_choice_id => null_choice_id,
                      :answer_text => n
                    }
                    ex_answer_pre[index].update(standard_option)
                  end
                  if new_delete_choice.count > 0 #表示填空题的答案有删减的
                    new_delete_choice.each do |d|
                      ex_answer_pre[d - 1].destroy
                    end
                  end
                end
              else
                null_choice_text.each do |n|
                  standard_option = {
                    :exercise_question_id => @exercise_question.id,
                    :exercise_choice_id => null_choice_id,
                    :answer_text => n
                  }
                  question_standard_answer = ExerciseStandardAnswer.new(standard_option)
                  question_standard_answer.save!
                end
              end
            end
          end
        end
      end
      if @exercise_question.question_type == Exercise::SUBJECTIVE #主观题
        main_standard_answer = standard_answer.present? ? standard_answer.first : nil
        if @exercise_answers_array.present?
          @exercise_answers_array.first.update_attribute("answer_text", main_standard_answer)
        else
          standard_option = {
            :exercise_question_id => @exercise_question.id,
            :answer_text => main_standard_answer,
          }
          question_standard_answer = ExerciseStandardAnswer.new(standard_option)
          question_standard_answer.save!
        end
      elsif @exercise_question.question_type == Exercise::PRACTICAL
        question_score = 0
        shixun_name = params[:shixun_name] || @exercise_question.shixun_name
        @exercise_question.exercise_shixun_challenges.each_with_index do |challenge, index|
          challenge.question_score = params[:question_scores][index].to_f.round(1)
          challenge.save!
          question_score += params[:question_scores][index].to_f.round(1)
        end
        @exercise_question.question_score = question_score
        @exercise_question.shixun_name = shixun_name
        @exercise_question.save!
      elsif @exercise_question.question_type == Exercise::PROGRAM
        if @exercise_question.hack.present?
          @exercise_question.hack.update!(name: params[:question_title], description: params[:description])
        end
      end

      #当试卷已发布时（试卷的总状态),当标准答案修改时，如有已提交的学生，需重新计算分数.

      if standard_answer_change && @exercise.exercise_status >= Exercise::PUBLISHED
        # ex_users_committed = @exercise.exercise_users.exercise_user_committed
        # if ex_users_committed.size > 0
        #   ex_users_committed.each do |ex_user|
        #     update_objective_score = update_single_score(@exercise_question,ex_user.user_id,standard_answer)
        #     if update_objective_score != 0
        #       objective_score = ex_user.objective_score
        #       new_objective_score = objective_score + update_objective_score
        #       total_score = ex_user.score + update_objective_score
        #       total_score = total_score < 0.0 ? 0.0 : total_score
        #       ex_user.update_attributes(objective_score:new_objective_score,score:total_score)
        #     end
        #   end
        # end
        normal_status(3, "修改了标准答案\n是否重新计算学生答题的成绩？")
      else
        normal_status(0, "试卷更新成功！")
      end
    end
  end

  def update_scores
    ActiveRecord::Base.transaction do
      begin
        standard_answer = params[:standard_answers]
        ex_users_committed = @exercise.exercise_users.exercise_user_committed
        if ex_users_committed.size > 0
          ex_users_committed.each do |ex_user|
            update_objective_score = update_single_score(@exercise_question, ex_user.user_id, standard_answer)
            if update_objective_score != 0
              objective_score = ex_user.objective_score
              new_objective_score = objective_score + update_objective_score
              total_score = ex_user.score + update_objective_score
              total_score = total_score < 0.0 ? 0.0 : total_score
              ex_user.update!(objective_score: new_objective_score, score: total_score)
            end
          end
        end
        normal_status(0, "学生成绩更新成功")
      rescue Exception => e
        uid_logger_error(e.message)
        tip_exception("答案删除失败！")
      end
    end
  end

  # 试卷问题的上下移动
  def up_down
    ActiveRecord::Base.transaction do
      begin
        opr = params[:opr]
        current_q_p = @exercise_question.question_number.to_i #问题的当前位置
        if @exercise.exercise_status.to_i == Exercise::UNPUBLISHED
          if opr.present?
            if opr.to_s == "up"
              last_q_p = @exercise.exercise_questions.find_by(question_number: (current_q_p - 1)) # 当前问题的前一个问题
              if last_q_p.present?
                @exercise_question.update_attribute('question_number', (current_q_p - 1))
                last_q_p.update_attribute('question_number', current_q_p) # 重新获取当前问题的位置
                normal_status(0, "问题上移成功！")
              else
                normal_status(-1, "移动失败，已经是第一个问题了！")
              end
            elsif opr.to_s == "down"
              next_q_p = @exercise.exercise_questions.find_by(question_number: (current_q_p + 1)) # 当前问题的前一个问题
              if next_q_p.present?
                @exercise_question.update_attribute('question_number', (current_q_p + 1))
                next_q_p.update_attribute('question_number', current_q_p)
                normal_status(0, "问题下移成功！")
              else
                normal_status(-1, "移动失败，已经是最后一个问题了！")
              end
            end
          else
            normal_status(-1, "移动失败，请输入参数")
          end
        else
          normal_status(-1, "已发布的不能移动问题")
        end
      rescue Exception => e
        uid_logger_error(e.message)
        tip_exception("问题移动失败！")
      end
    end
  end

  #试卷选项的删除
  def delete_answer
    ActiveRecord::Base.transaction do
      begin
        choice_d_id = params[:choice_no].to_i # 选项的当前位置
        question_choices = @exercise_question.exercise_choices
        delete_answer = question_choices.find_by(choice_position: choice_d_id)
        left_choices = question_choices.where("choice_position > ? ", choice_d_id)
        left_choices.update_all("choice_position = choice_position - 1") if left_choices
        if delete_answer.destroy
          normal_status(0, "答案删除成功！")
        else
          normal_status(-1, "答案删除失败！")
        end
      rescue Exception => e
        uid_logger_error(e.message)
        tip_exception("答案删除失败！")
      end
    end
  end

  #试卷问题的删除
  def destroy
    ActiveRecord::Base.transaction do
      begin
        question_d_id = @exercise_question.question_number.to_i #问题的当前位置
        exercise_questions = @exercise.exercise_questions
        left_questions = exercise_questions.where("question_number > ?", question_d_id)
        left_questions.update_all("question_number = question_number - 1") if left_questions
        if @exercise_question.destroy
          normal_status(0, "问题删除成功！")
        else
          normal_status(-1, "问题删除失败！")
        end
      rescue Exception => e
        uid_logger_error(e.message)
        tip_exception("问题删除失败！")
      end
    end
  end

  #老师调分窗口
  def adjust_score
    ActiveRecord::Base.transaction do
      ex_all_scores = @exercise.exercise_questions.pluck(:question_score).sum
      ex_obj_score = @exercise_current_user.objective_score #全部客观题得分
      ex_subj_score = @exercise_current_user.subjective_score < 0.0 ? 0.0 : @exercise_current_user.subjective_score #全部主观题得分
      ex_answers = @exercise_question.exercise_answers.where(user_id: @user_id) #当前用户对该问题的回答
      # 当前试卷用户所有的主观题的回答
      all_subjective_exercise_answers = @exercise.exercise_answers.where(user_id: @user_id).joins(:exercise_question).where(exercise_questions: {question_type: Exercise::SUBJECTIVE})
      # 当前试卷用户的所有主观题
      if @exercise.is_random?
        all_subjective_questions = @exercise_current_user.exercise_questions.subjectives
      else
        all_subjective_questions = @exercise.exercise_questions.subjectives
      end
      if @exercise_question.question_type == Exercise::MULTIPLE
        if ex_answers.present? #学生有回答时 取学生的答题得分，否则0分
          answer_choice_array = []
          ex_answers.each do |a|
            if a.try(:exercise_choice).try(:choice_position).present?
              answer_choice_array.push(a&.exercise_choice&.choice_position) #学生答案的位置
            end
          end
          user_answer_content = answer_choice_array.reject(&:blank?).sort
          standard_answer = @exercise_question.exercise_standard_answers.pluck(:exercise_choice_id).sort
          if standard_answer.size == 1 # 老数据需要判断学生答题是否正确, 正确取原题得分，否则是0分
            standard_answer = standard_answer.first.to_s.split("").map(&:to_i).sort
            if user_answer_content == standard_answer
              ex_answer_old = @exercise_question.question_score
            else
              ex_answer_old = 0
            end
          else # 新多选题只需取第一条答题记录的得分
            ex_answer_old = ex_answers.first.score > 0 ? ex_answers.first.score : 0
          end
          ex_answers.update_all(:score => @c_score) #所有的正确选项需重新更新
        else
          answer_option = {
            :user_id => @user_id,
            :exercise_question_id => @exercise_question.id,
            :score => @c_score,
            :answer_text => ""
          }
          ExerciseAnswer.create!(answer_option)
          # exercise_answer = ExerciseAnswer.find_or_initialize_by(answer_option.slice(:user_id, :exercise_question_id))
          # exercise_answer.assign_attributes(answer_option.slice(:score, :answer_text))
          # exercise_answer.save!
          ex_answer_old = 0
        end
        if ex_obj_score <= 0.0
          new_obj_score = @c_score
        else
          new_obj_score = ex_obj_score - ex_answer_old + @c_score
        end

        total_scores = new_obj_score + ex_subj_score
        if total_scores < 0.0
          total_scores = 0.0
        elsif total_scores > ex_all_scores
          total_scores = ex_all_scores
        end
        ex_scores = {
          :objective_score => new_obj_score,
          :score => total_scores
        }
        @exercise_current_user.update!(ex_scores)

      elsif @exercise_question.question_type == Exercise::COMPLETION #当为填空题，更新问题的总分,

        if ex_answers.exists?
          ex_answer_old = ex_answers.score_reviewed.pluck(:score).sum #每一关的得分总和
          each_right_score = (@c_score / ex_answers.count.to_f) #调分后，平均每关的分数
          new_obj_score = ex_obj_score - ex_answer_old + @c_score
          ex_answers.update_all(:score => each_right_score) #所有的正确选项需重新更新
        else #如果学生未答，则创建新的答题记录
          answer_option = {
            :user_id => @user_id,
            :exercise_question_id => @exercise_question.id,
            :score => @c_score,
            :answer_text => ""
          }
          ExerciseAnswer.create!(answer_option)
          new_obj_score = ex_obj_score + @c_score
        end

        total_scores = new_obj_score + ex_subj_score
        if total_scores < 0.0
          total_scores = 0.0
        elsif total_scores > ex_all_scores
          total_scores = ex_all_scores
        end
        ex_scores = {
          :objective_score => new_obj_score,
          :score => total_scores
        }
        @exercise_current_user.update!(ex_scores)
      elsif @exercise_question.question_type == Exercise::SUBJECTIVE #当为主观题时
        if ex_answers.exists?
          ex_answers_old_score = ex_answers.first.score > 0.0 ? ex_answers.first.score : 0.0 #原分数小于0，取0
          new_sub_score = ex_subj_score - ex_answers_old_score + @c_score #原全部主观题总分减去原该主观题得分再加调分后的分数，即为当前全部主观题得分
          ex_answers.first.update_attribute("score", @c_score)
        else #如果学生未答，则创建新的答题记录
          answer_option = {
            :user_id => @user_id,
            :exercise_question_id => @exercise_question.id,
            :score => @c_score,
            :answer_text => ""
          }
          ExerciseAnswer.create!(answer_option)
          new_sub_score = ex_subj_score + @c_score
        end
        total_scores = ex_obj_score + new_sub_score
        if total_scores < 0.0
          total_scores = 0.0
        elsif total_scores > ex_all_scores
          total_scores = ex_all_scores
        end
        # 判断当前学生的试卷中是否所有的主观题都已评阅,如果所有主观题都评阅过才将reviewed置为true(主观题对应的回答的分数大于等于0即为已评阅，因为默认为-1)
        if (all_subjective_exercise_answers.reload.size == all_subjective_questions.size) && all_subjective_exercise_answers.all?{|ex| ex.score>=0}
          reviewed = true
        else
          reviewed = false
        end
        ex_scores = {
          :subjective_score => new_sub_score,
          :score => total_scores,
          :subjective_reviewed => 1,
          :reviewed => reviewed
        }
        @exercise_current_user.update!(ex_scores)

      elsif @exercise_question.question_type == Exercise::PRACTICAL
        ex_answers = @exercise_question.exercise_shixun_answers.where(user_id: @user_id, exercise_shixun_challenge_id: @shixun_a_id)

        if ex_answers.present? #当为实训题时
          ex_shixun_old_score = ex_answers.first.score > 0.0 ? ex_answers.first.score : 0.0
          new_obj_score = ex_obj_score - ex_shixun_old_score + @c_score
          ex_answers.first.update_attribute("score", @c_score)
        else
          ex_shixun_option = {
            :exercise_question_id => @exercise_question.id,
            :user_id => @user_id,
            :exercise_shixun_challenge_id => @shixun_a_id,
            :score => @c_score,
            :status => 0
          }
          ExerciseShixunAnswer.create!(ex_shixun_option)
          new_obj_score = ex_obj_score + @c_score
        end
        total_scores = new_obj_score + ex_subj_score
        if total_scores < 0.0
          total_scores = 0.0
        elsif total_scores > ex_all_scores
          total_scores = ex_all_scores
        end
        ex_scores = {
          :objective_score => new_obj_score,
          :score => total_scores
        }
        @exercise_current_user.update!(ex_scores)
      elsif @exercise_question.question_type == Exercise::PROGRAM
        if ex_answers.exists?
          ex_pro_old_score = ex_answers.first.score > 0.0 ? ex_answers.first.score : 0.0
          new_obj_score = ex_obj_score - ex_pro_old_score + @c_score
          ex_answers.first.update_attribute("score", @c_score)
        else #如果学生未答，则创建新的答题记录
          answer_option = {
            :user_id => @user_id,
            :exercise_question_id => @exercise_question.id,
            :score => @c_score,
            :answer_text => ""
          }
          ExerciseAnswer.create!(answer_option)
          new_obj_score = ex_obj_score + @c_score
        end

        total_scores = new_obj_score + ex_subj_score
        if total_scores < 0.0
          total_scores = 0.0
        elsif total_scores > ex_all_scores
          total_scores = ex_all_scores
        end
        ex_scores = {
          :objective_score => new_obj_score,
          :score => total_scores
        }
        @exercise_current_user.update!(ex_scores)
      end
      comments = params[:comment]

      if @exercise_question.question_type == Exercise::PRACTICAL
        shixun_answer = ExerciseShixunAnswer.find_by(exercise_question_id: @exercise_question.id, user_id: @user_id, exercise_shixun_challenge_id: @shixun_a_id)
        answer_comment = shixun_answer&.exercise_answer_comments&.take
      else
        question_answer = ExerciseAnswer.find_by(exercise_question_id: @exercise_question.id, user_id: @user_id)
        answer_comment = question_answer&.exercise_answer_comments&.take
      end

      if answer_comment.present?
        answer_comment.update!(:comment => comments, :score => @c_score)
      else
        ExerciseAnswerComment.create!(:comment => comments, :score => @c_score, :user_id => current_user.id,
                                      :exercise_question_id => @exercise_question.id, :exercise_shixun_answer_id => shixun_answer&.id,
                                      :exercise_answer_id => question_answer&.id)

        unless Tiding.where(user_id: @user_id, trigger_user_id: current_user.id, parent_container_id: @exercise.id, parent_container_type: "ExerciseScore").exists?
          Tiding.create!(user_id: @user_id, trigger_user_id: current_user.id, container_id: @exercise.id,
                         container_type: "Exercise", parent_container_id: @exercise.id,
                         parent_container_type: "ExerciseScore", belong_container_id: @course.id,
                         belong_container_type: 'Course', tiding_type: "Exercise")
        end
      end

      # question_comment = @exercise_question.exercise_answer_comments&.first
      #
      # if question_comment.present?
      #   comment_option = {
      #     :comment => comments,
      #     :score => @c_score,
      #     :exercise_answer_id => ex_answers.present? ? ex_answers.first.id : nil,
      #     :user_id => current_user.id
      #   }
      #   question_comment.update!(comment_option)
      #   @exercise_comments = question_comment
      # else
      #   ex_answer_comment_id = @exercise_question.exercise_answers.find_by(user_id: @user_id).try(:id)
      #   comment_option = {
      #     :user_id => current_user.id,
      #     :comment => comments,
      #     :score => @c_score,
      #     :exercise_question_id => @exercise_question.id,
      #     :exercise_shixun_answer_id => @shixun_a_id.present? ? @shixun_a_id : nil,
      #     :exercise_answer_id => ex_answer_comment_id
      #   }
      #   @exercise_comments = ExerciseAnswerComment.new(comment_option)
      #   @exercise_comments.save!
      #
      #   # 给被评阅人发送消息，同一个教师评阅无需重复发消息
      #
      #   unless Tiding.where(user_id: @user_id, trigger_user_id: current_user.id, parent_container_id: @exercise.id, parent_container_type: "ExerciseScore").exists?
      #     Tiding.create!(user_id: @user_id, trigger_user_id: current_user.id, container_id: @exercise.id,
      #                    container_type: "Exercise", parent_container_id: @exercise.id,
      #                    parent_container_type: "ExerciseScore", belong_container_id: @course.id,
      #                    belong_container_type: 'Course', tiding_type: "Exercise")
      #   end
      #
      # end
    end
  end

  def batch_adjust_score_copy
    ActiveRecord::Base.transaction do
      # 以user_id为key,用户对应的exericse_answers数组为value的hash, 替代 ex_answers
      exercise_answers_hash = @exercise_question.exercise_answers.
          includes(:exercise_answer_comments).
          select(:score, :user_id).group_by{|ex_answer| ex_answer.user_id}

      exercise_shixun_answers_hash = @exercise_question.exercise_shixun_answers.
          includes(:exercise_answer_comments).
          where(exercise_shixun_challenge_id: @shixun_a_id).
          select(:score, :user_id).group_by{|ex_answer| ex_answer.user_id}

      exercise_question_id = @exercise_question.id

      current_user_id = current_user.id

      # 以user_id为key,用户对应的主观题的exericse_answers数组为value的hash, 替代 all_subjective_exercise_answers
      all_subjective_exercise_answers_hash = @exercise.subjective_exercise_answers.select(:score, :user_id).group_by{|ex_answer| ex_answer.user_id}
      # 用于填空题,有得分的回答集合 替代 ex_answers.score_reviewed
      ex_answers_score_reviewed_hash = @exercise_question.exercise_answers.score_reviewed.select(:score, :user_id).group_by{|ex_answer| ex_answer.user_id}
      # 该试卷下所有主观题
      all_subjective_exercise_questions = @exercise.exercise_questions.subjectives
      @exercise_users.each do |exercise_user|
        ex_all_scores = @exercise.exercise_questions.pluck(:question_score).sum
        ex_obj_score = exercise_user.objective_score #全部客观题得分
        ex_subj_score = exercise_user.subjective_score < 0.0 ? 0.0 : exercise_user.subjective_score #全部主观题得分
        # ex_answers = @exercise_question.exercise_answers.where(user_id: exercise_user.user_id) #当前用户答案的得分
        # 该用户所有的主观题的回答
        # all_subjective_exercise_answers = @exercise.subjective_exercise_answers.where(user_id: exercise_user.user_id)
        if @exercise_question.question_type == Exercise::SUBJECTIVE #当为主观题时
          if exercise_answers_hash[exercise_user.user_id].present?
            ex_answers_old_score = exercise_answers_hash[exercise_user.user_id].first.score > 0.0 ? exercise_answers_hash[exercise_user.user_id].first.score : 0.0 #原分数小于0，取0
            new_sub_score = ex_subj_score - ex_answers_old_score + @c_score #原全部主观题总分减去原该主观题得分再加调分后的分数，即为当前全部主观题得分
            exercise_answers_hash[exercise_user.user_id].first.update_attribute("score", @c_score)
          else #如果学生未答，则创建新的答题记录
            answer_option = {
                :user_id => exercise_user.user_id,
                :exercise_question_id => exercise_question_id,
                :score => @c_score,
                :answer_text => ""
            }
            ExerciseAnswer.create!(answer_option)
            new_sub_score = ex_subj_score + @c_score
          end
          total_scores = ex_obj_score + new_sub_score
          if total_scores < 0.0
            total_scores = 0.0
          elsif total_scores > ex_all_scores
            total_scores = ex_all_scores
          end
          # 判断当前学生的试卷中是否所有的主观题都已评阅,如果所有主观题都评阅过才将reviewed置为true(主观题对应的回答的分数大于等于0即为已评阅，因为默认为-1)
          if (all_subjective_exercise_answers_hash[exercise_user.user_id]&.size == all_subjective_exercise_questions.size) && all_subjective_exercise_answers_hash[exercise_user.user_id]&.all?{|ex| ex.score>=0}
            reviewed = true
          else
            reviewed = false
          end
          ex_scores = {
              :subjective_score => new_sub_score,
              :score => total_scores,
              :subjective_reviewed => 1,
              :reviewed => reviewed
          }
          exercise_user.update!(ex_scores)
        end
        #   if @exercise_question.question_type == Exercise::MULTIPLE
        #   if exercise_answers_hash[exercise_user.user_id].present? #学生有回答时 取学生的答题得分，否则0分
        #     answer_choice_array = []
        #     exercise_answers_hash[exercise_user.user_id].each do |a|
        #       if a.try(:exercise_choice).try(:choice_position).present?
        #         answer_choice_array.push(a&.exercise_choice&.choice_position) #学生答案的位置
        #       end
        #     end
        #     user_answer_content = answer_choice_array.reject(&:blank?).sort
        #     standard_answer = @exercise_question.exercise_standard_answers.pluck(:exercise_choice_id).sort
        #     if standard_answer.size == 1 # 老数据需要判断学生答题是否正确, 正确取原题得分，否则是0分
        #       standard_answer = standard_answer.first.to_s.split("").map(&:to_i).sort
        #       if user_answer_content == standard_answer
        #         ex_answer_old = @exercise_question.question_score
        #       else
        #         ex_answer_old = 0
        #       end
        #     else # 新多选题只需取第一条答题记录的得分
        #       ex_answer_old = exercise_answers_hash[exercise_user.user_id].first.score > 0 ? exercise_answers_hash[exercise_user.user_id].first.score : 0
        #     end
        #     exercise_answers_hash[exercise_user.user_id].update_all(:score => @c_score) #所有的正确选项需重新更新
        #   else
        #     answer_option = {
        #         :user_id => exercise_user.user_id,
        #         :exercise_question_id => exercise_question_id,
        #         :score => @c_score,
        #         :answer_text => ""
        #     }
        #     ExerciseAnswer.create!(answer_option)
        #     # exercise_answer = ExerciseAnswer.find_or_initialize_by(answer_option.slice(:user_id, :exercise_question_id))
        #     # exercise_answer.assign_attributes(answer_option.slice(:score, :answer_text))
        #     # exercise_answer.save!
        #     ex_answer_old = 0
        #   end
        #   if ex_obj_score <= 0.0
        #     new_obj_score = @c_score
        #   else
        #     new_obj_score = ex_obj_score - ex_answer_old + @c_score
        #   end
        #
        #   total_scores = new_obj_score + ex_subj_score
        #   if total_scores < 0.0
        #     total_scores = 0.0
        #   elsif total_scores > ex_all_scores
        #     total_scores = ex_all_scores
        #   end
        #   ex_scores = {
        #       :objective_score => new_obj_score,
        #       :score => total_scores
        #   }
        #   @exercise_current_user.update!(ex_scores)
        #
        # elsif @exercise_question.question_type == Exercise::COMPLETION #当为填空题，更新问题的总分,
        #
        #   if exercise_answers_hash[exercise_user.user_id].exists?
        #     ex_answer_old = ex_answers_score_reviewed_hash[exercise_user.user_id].pluck(:score).sum #每一关的得分总和
        #     each_right_score = (@c_score / exercise_answers_hash[exercise_user.user_id].count.to_f) #调分后，平均每关的分数
        #     new_obj_score = ex_obj_score - ex_answer_old + @c_score
        #     exercise_answers_hash[exercise_user.user_id].update_all(:score => each_right_score) #所有的正确选项需重新更新
        #   else #如果学生未答，则创建新的答题记录
        #     answer_option = {
        #         :user_id => exercise_user.user_id,
        #         :exercise_question_id => exercise_question_id,
        #         :score => @c_score,
        #         :answer_text => ""
        #     }
        #     ExerciseAnswer.create!(answer_option)
        #     new_obj_score = ex_obj_score + @c_score
        #   end
        #
        #   total_scores = new_obj_score + ex_subj_score
        #   if total_scores < 0.0
        #     total_scores = 0.0
        #   elsif total_scores > ex_all_scores
        #     total_scores = ex_all_scores
        #   end
        #   ex_scores = {
        #       :objective_score => new_obj_score,
        #       :score => total_scores
        #   }
        #   @exercise_current_user.update!(ex_scores)
        # elsif @exercise_question.question_type == Exercise::SUBJECTIVE #当为主观题时
        #   if exercise_answers_hash[exercise_user.user_id].present?
        #     ex_answers_old_score = exercise_answers_hash[exercise_user.user_id].first.score > 0.0 ? exercise_answers_hash[exercise_user.user_id].first.score : 0.0 #原分数小于0，取0
        #     new_sub_score = ex_subj_score - ex_answers_old_score + @c_score #原全部主观题总分减去原该主观题得分再加调分后的分数，即为当前全部主观题得分
        #     exercise_answers_hash[exercise_user.user_id].first.update_attribute("score", @c_score)
        #   else #如果学生未答，则创建新的答题记录
        #     answer_option = {
        #         :user_id => exercise_user.user_id,
        #         :exercise_question_id => exercise_question_id,
        #         :score => @c_score,
        #         :answer_text => ""
        #     }
        #     ExerciseAnswer.create!(answer_option)
        #     new_sub_score = ex_subj_score + @c_score
        #   end
        #   total_scores = ex_obj_score + new_sub_score
        #   if total_scores < 0.0
        #     total_scores = 0.0
        #   elsif total_scores > ex_all_scores
        #     total_scores = ex_all_scores
        #   end
        #   # 判断当前学生的试卷中是否所有的主观题都已评阅,如果所有主观题都评阅过才将reviewed置为true(主观题对应的回答的分数大于等于0即为已评阅，因为默认为-1)
        #   if (all_subjective_exercise_answers_hash[exercise_user.user_id]&.size == all_subjective_exercise_questions.size) && all_subjective_exercise_answers_hash[exercise_user.user_id]&.all?{|ex| ex.score>=0}
        #     reviewed = true
        #   else
        #     reviewed = false
        #   end
        #   ex_scores = {
        #       :subjective_score => new_sub_score,
        #       :score => total_scores,
        #       :subjective_reviewed => 1,
        #       :reviewed => reviewed
        #   }
        #   exercise_user.update!(ex_scores)
        #
        # elsif @exercise_question.question_type == Exercise::PRACTICAL
        #   ex_answers = @exercise_question.exercise_shixun_answers.where(user_id: exercise_user.user_id, exercise_shixun_challenge_id: @shixun_a_id)
        #
        #   if ex_answers.present? #当为实训题时
        #     ex_shixun_old_score = ex_answers.first.score > 0.0 ? ex_answers.first.score : 0.0
        #     new_obj_score = ex_obj_score - ex_shixun_old_score + @c_score
        #     ex_answers.first.update_attribute("score", @c_score)
        #   else
        #     ex_shixun_option = {
        #         :exercise_question_id => exercise_question_id,
        #         :user_id => exercise_user.user_id,
        #         :exercise_shixun_challenge_id => @shixun_a_id,
        #         :score => @c_score,
        #         :status => 0
        #     }
        #     ExerciseShixunAnswer.create!(ex_shixun_option)
        #     new_obj_score = ex_obj_score + @c_score
        #   end
        #   total_scores = new_obj_score + ex_subj_score
        #   if total_scores < 0.0
        #     total_scores = 0.0
        #   elsif total_scores > ex_all_scores
        #     total_scores = ex_all_scores
        #   end
        #   ex_scores = {
        #       :objective_score => new_obj_score,
        #       :score => total_scores
        #   }
        #   exercise_user.update!(ex_scores)
        # elsif @exercise_question.question_type == Exercise::PROGRAM
        #   if exercise_answers_hash[exercise_user.user_id].exists?
        #     ex_pro_old_score = exercise_answers_hash[exercise_user.user_id].first.score > 0.0 ? exercise_answers_hash[exercise_user.user_id].first.score : 0.0
        #     new_obj_score = ex_obj_score - ex_pro_old_score + @c_score
        #     exercise_answers_hash[exercise_user.user_id].first.update_attribute("score", @c_score)
        #   else #如果学生未答，则创建新的答题记录
        #     answer_option = {
        #         :user_id => exercise_user.user_id,
        #         :exercise_question_id => exercise_question_id,
        #         :score => @c_score,
        #         :answer_text => ""
        #     }
        #     ExerciseAnswer.create!(answer_option)
        #     new_obj_score = ex_obj_score + @c_score
        #   end
        #
        #   total_scores = new_obj_score + ex_subj_score
        #   if total_scores < 0.0
        #     total_scores = 0.0
        #   elsif total_scores > ex_all_scores
        #     total_scores = ex_all_scores
        #   end
        #   ex_scores = {
        #       :objective_score => new_obj_score,
        #       :score => total_scores
        #   }
        #   exercise_user.update!(ex_scores)
        # end
        comments = params[:comment]

        if @exercise_question.question_type == Exercise::PRACTICAL
          shixun_answer = exercise_shixun_answers_hash[exercise_user.user_id]&.first
          answer_comment = shixun_answer&.exercise_answer_comments&.take
        else
          question_answer = exercise_answers_hash[exercise_user.user_id]&.first
          answer_comment = question_answer&.exercise_answer_comments&.take
        end

        if answer_comment.present?
          answer_comment.update!(:comment => comments, :score => @c_score)
        else
          ExerciseAnswerComment.create!(:comment => comments, :score => @c_score, :user_id => current_user_id,
                                        :exercise_question_id => exercise_question_id, :exercise_shixun_answer_id => shixun_answer&.id,
                                        :exercise_answer_id => question_answer&.id)

          unless Tiding.where(user_id: exercise_user.user_id, trigger_user_id: current_user_id, parent_container_id: @exercise.id, parent_container_type: "ExerciseScore").exists?
            Tiding.create!(user_id: exercise_user.user_id, trigger_user_id: current_user_id, container_id: @exercise.id,
                           container_type: "Exercise", parent_container_id: @exercise.id,
                           parent_container_type: "ExerciseScore", belong_container_id: @course.id,
                           belong_container_type: 'Course', tiding_type: "Exercise")
          end
        end
      end
      normal_status("操作成功")
    end
  end

  def batch_adjust_score
    # 覆盖全部, 为true的话，如果学生试卷之前有过评阅则更新其评阅记录，否则不更新
    @cover_all = params[:cover_all]
    ActiveRecord::Base.transaction do
      # 以user_id为key,用户对应的exericse_answers数组为value的hash, 替代 ex_answers
      exercise_answers_hash = @exercise_question.exercise_answers.
          includes(:exercise_answer_comments).group_by{|ex_answer| ex_answer.user_id}

      exercise_shixun_answers_hash = @exercise_question.exercise_shixun_answers.
          includes(:exercise_answer_comments).
          where(exercise_shixun_challenge_id: @shixun_a_id).group_by{|ex_answer| ex_answer.user_id}

      exercise_question_id = @exercise_question.id

      current_user_id = current_user.id

      # 以user_id为key,用户对应的主观题的exericse_answers数组为value的hash, 替代 all_subjective_exercise_answers
      all_subjective_exercise_answers_hash = @exercise.subjective_exercise_answers.select(:id, :score, :user_id).group_by{|ex_answer| ex_answer.user_id}
      # 用于填空题,有得分的回答集合 替代 ex_answers.score_reviewed
      ex_answers_score_reviewed_hash = @exercise_question.exercise_answers.score_reviewed.select(:score, :user_id).group_by{|ex_answer| ex_answer.user_id}
      # 该试卷下所有主观题的数量
      all_subjective_exercise_questions_size = @exercise.exercise_questions.subjectives.size
      # 预先查出所有tiding,避免在循环中再次通过查询验证是否存在
      tiding_user_id_array = Tiding.where(
          user_id: @exercise_users.pluck(:user_id), trigger_user_id: current_user_id,
          parent_container_id: @exercise.id, parent_container_type: "ExerciseScore"
      ).pluck(:user_id)
      # 试卷总分
      ex_all_scores = @exercise.exercise_questions.pluck(:question_score).sum

      # 通知的属性数组,用于循环结束后批量创建tiding
      tiding_attrs = []
      # 试卷评论数组，用于循环结束后批量创建评论
      exercise_answer_comment_attrs = []
      # 试卷回答数组，用于循环结束后批量创建试卷回答
      exercise_answer_attrs = []
      # 批量更新试卷用户的sql
      exercise_user_update_sql = "UPDATE exercise_users SET "
      fields = %W(subjective_score score subjective_reviewed reviewed)
      fields_values = {}
      fields.each { |d| fields_values[d] = []}
      exercise_user_ids = []
      @exercise_users.each do |exercise_user|
        exercise_answers = exercise_answers_hash[exercise_user.user_id]
        exercise_shixun_answers = exercise_shixun_answers_hash[exercise_user.user_id]
        # 如果之前有过评阅且勾选了覆盖之前评阅,则更新或者创建分数
        ex_obj_score = exercise_user.objective_score #全部客观题得分
        ex_subj_score = exercise_user.subjective_score < 0.0 ? 0.0 : exercise_user.subjective_score #全部主观题得分
        # ex_answers = @exercise_question.exercise_answers.where(user_id: exercise_user.user_id) #当前用户答案的得分
        # 该用户所有的主观题的回答
        # all_subjective_exercise_answers = @exercise.subjective_exercise_answers.where(user_id: exercise_user.user_id)
        # 批量评阅现在只对主观题进行评阅
        if @exercise_question.question_type == Exercise::SUBJECTIVE #当为主观题时
          @min_score = @min_score.to_i
          @max_score = @max_score.to_i
          @score = (@min_score..@max_score).to_a.sample
          if exercise_answers_hash[exercise_user.user_id].present?
            # 该用户的回答
            exercise_answer = exercise_answers_hash[exercise_user.user_id].first
            # 如果之前有过评阅而且禁止覆盖之前的评阅,那么将跳过该学生的评阅
            next if (exercise_answer.score > 0.0) && !@cover_all

            ex_answers_old_score = exercise_answer.score > 0.0 ? exercise_answer.score : 0.0 #原分数小于0，取0
            new_sub_score = ex_subj_score - ex_answers_old_score + @score #原全部主观题总分减去原该主观题得分再加调分后的分数，即为当前全部主观题得分
            exercise_answer.update_attribute("score", @score)
          else #如果学生未答，则创建新的答题记录
            exercise_answer_attrs << {
                :user_id => exercise_user.user_id,
                :exercise_question_id => exercise_question_id,
                :score => @score,
                :answer_text => ""
            }
            new_sub_score = ex_subj_score + @score
          end
          total_scores = ex_obj_score + new_sub_score
          if total_scores < 0.0
            total_scores = 0.0
          elsif total_scores > ex_all_scores
            total_scores = ex_all_scores
          end
          # 判断当前学生的试卷中是否所有的主观题都已评阅,如果所有主观题都评阅过才将reviewed置为true(主观题对应的回答的分数大于等于0即为已评阅，因为默认为-1), 或者 整个试卷只有一道主观题，那么也可以直接将状态置为已评阅
          if ((all_subjective_exercise_answers_hash[exercise_user.user_id].try(:reject){|ea| ea.id == exercise_answer&.id}&.size == (all_subjective_exercise_questions_size-1)) &&
                  all_subjective_exercise_answers_hash[exercise_user.user_id].try(:reject){|ea| ea.id == exercise_answer&.id}.try(:all?){|ex| ex.score>=0}) || (all_subjective_exercise_questions_size==1)
            reviewed = 1
          else
            reviewed = 0
          end
          exercise_user_ids << exercise_user.id
          ex_scores = {
              :subjective_score => new_sub_score,
              :score => total_scores,
              :subjective_reviewed => 1,
              :reviewed => reviewed
          }
          fields.each do |d|
            fields_values[d] << " WHEN #{exercise_user.id} THEN #{ex_scores[d.to_sym] || 'null'} "
          end

        end

        comments = params[:comment]

        question_answer = exercise_answers_hash[exercise_user.user_id]&.first
        answer_comment = question_answer&.exercise_answer_comments&.take

        if answer_comment.present?
          answer_comment.update!(:comment => comments, :score => @c_score)
        else
          exercise_answer_comment_attrs << {
              :comment => comments, :score => @c_score, :user_id => current_user_id,
              :exercise_question_id => exercise_question_id,
              :exercise_answer_id => question_answer&.id,
              :exercise_user_id => exercise_user.id
          }
          unless tiding_user_id_array.include? exercise_user.user_id
            tiding_attrs << {
              user_id: exercise_user.user_id, trigger_user_id: current_user_id, container_id: @exercise.id,
              container_type: "Exercise", parent_container_id: @exercise.id,
              parent_container_type: "ExerciseScore", belong_container_id: @course.id,
              belong_container_type: 'Course', tiding_type: "Exercise"
            }
          end
        end
        # if @exercise_question.question_type == Exercise::MULTIPLE
        #   if exercise_answers_hash[exercise_user.user_id].present? #学生有回答时 取学生的答题得分，否则0分
        #     answer_choice_array = []
        #     exercise_answers_hash[exercise_user.user_id].each do |a|
        #       if a.try(:exercise_choice).try(:choice_position).present?
        #         answer_choice_array.push(a&.exercise_choice&.choice_position) #学生答案的位置
        #       end
        #     end
        #     user_answer_content = answer_choice_array.reject(&:blank?).sort
        #     standard_answer = @exercise_question.exercise_standard_answers.pluck(:exercise_choice_id).sort
        #     if standard_answer.size == 1 # 老数据需要判断学生答题是否正确, 正确取原题得分，否则是0分
        #       standard_answer = standard_answer.first.to_s.split("").map(&:to_i).sort
        #       if user_answer_content == standard_answer
        #         ex_answer_old = @exercise_question.question_score
        #       else
        #         ex_answer_old = 0
        #       end
        #     else # 新多选题只需取第一条答题记录的得分
        #       ex_answer_old = exercise_answers_hash[exercise_user.user_id].first.score > 0 ? exercise_answers_hash[exercise_user.user_id].first.score : 0
        #     end
        #     exercise_answers_hash[exercise_user.user_id].update_all(:score => @c_score) #所有的正确选项需重新更新
        #   else
        #     answer_option = {
        #         :user_id => exercise_user.user_id,
        #         :exercise_question_id => exercise_question_id,
        #         :score => @c_score,
        #         :answer_text => ""
        #     }
        #     ExerciseAnswer.create!(answer_option)
        #     # exercise_answer = ExerciseAnswer.find_or_initialize_by(answer_option.slice(:user_id, :exercise_question_id))
        #     # exercise_answer.assign_attributes(answer_option.slice(:score, :answer_text))
        #     # exercise_answer.save!
        #     ex_answer_old = 0
        #   end
        #   if ex_obj_score <= 0.0
        #     new_obj_score = @c_score
        #   else
        #     new_obj_score = ex_obj_score - ex_answer_old + @c_score
        #   end
        #
        #   total_scores = new_obj_score + ex_subj_score
        #   if total_scores < 0.0
        #     total_scores = 0.0
        #   elsif total_scores > ex_all_scores
        #     total_scores = ex_all_scores
        #   end
        #   ex_scores = {
        #       :objective_score => new_obj_score,
        #       :score => total_scores
        #   }
        #   @exercise_current_user.update!(ex_scores)
        # elsif @exercise_question.question_type == Exercise::COMPLETION #当为填空题，更新问题的总分,
        #
        #   if exercise_answers_hash[exercise_user.user_id].exists?
        #     ex_answer_old = ex_answers_score_reviewed_hash[exercise_user.user_id].pluck(:score).sum #每一关的得分总和
        #     each_right_score = (@c_score / exercise_answers_hash[exercise_user.user_id].count.to_f) #调分后，平均每关的分数
        #     new_obj_score = ex_obj_score - ex_answer_old + @c_score
        #     exercise_answers_hash[exercise_user.user_id].update_all(:score => each_right_score) #所有的正确选项需重新更新
        #   else #如果学生未答，则创建新的答题记录
        #     answer_option = {
        #         :user_id => exercise_user.user_id,
        #         :exercise_question_id => exercise_question_id,
        #         :score => @c_score,
        #         :answer_text => ""
        #     }
        #     ExerciseAnswer.create!(answer_option)
        #     new_obj_score = ex_obj_score + @c_score
        #   end
        #
        #   total_scores = new_obj_score + ex_subj_score
        #   if total_scores < 0.0
        #     total_scores = 0.0
        #   elsif total_scores > ex_all_scores
        #     total_scores = ex_all_scores
        #   end
        #   ex_scores = {
        #       :objective_score => new_obj_score,
        #       :score => total_scores
        #   }
        #   @exercise_current_user.update!(ex_scores)
        # elsif @exercise_question.question_type == Exercise::SUBJECTIVE #当为主观题时
        #   @min_score = @min_score.to_i
        #   @max_score = @max_score.to_i
        #   @score = (@min_score..@max_score).to_a.sample
        #   if exercise_answers_hash[exercise_user.user_id].present?
        #     ex_answers_old_score = exercise_answers_hash[exercise_user.user_id].first.score > 0.0 ? exercise_answers_hash[exercise_user.user_id].first.score : 0.0 #原分数小于0，取0
        #     new_sub_score = ex_subj_score - ex_answers_old_score + @c_score #原全部主观题总分减去原该主观题得分再加调分后的分数，即为当前全部主观题得分
        #     exercise_answers_hash[exercise_user.user_id].first.update_attribute("score", @c_score)
        #   else #如果学生未答，则创建新的答题记录
        #     answer_option = {
        #         :user_id => exercise_user.user_id,
        #         :exercise_question_id => exercise_question_id,
        #         :score => @score,
        #         :answer_text => ""
        #     }
        #     ExerciseAnswer.create!(answer_option)
        #     new_sub_score = ex_subj_score + @score
        #   end
        #   total_scores = ex_obj_score + new_sub_score
        #   if total_scores < 0.0
        #     total_scores = 0.0
        #   elsif total_scores > ex_all_scores
        #     total_scores = ex_all_scores
        #   end
        #   # 判断当前学生的试卷中是否所有的主观题都已评阅,如果所有主观题都评阅过才将reviewed置为true(主观题对应的回答的分数大于等于0即为已评阅，因为默认为-1)
        #   if (all_subjective_exercise_answers_hash[exercise_user.user_id]&.size == all_subjective_exercise_questions_size) && all_subjective_exercise_answers_hash[exercise_user.user_id]&.all?{|ex| ex.score>=0}
        #     reviewed = true
        #   else
        #     reviewed = false
        #   end
        #   ex_scores = {
        #       :subjective_score => new_sub_score,
        #       :score => total_scores,
        #       :subjective_reviewed => 1,
        #       :reviewed => reviewed
        #   }
        #   exercise_user.update!(ex_scores)
        # elsif @exercise_question.question_type == Exercise::PRACTICAL
        #   ex_answers = @exercise_question.exercise_shixun_answers.where(user_id: exercise_user.user_id, exercise_shixun_challenge_id: @shixun_a_id)
        #
        #   if ex_answers.present? #当为实训题时
        #     ex_shixun_old_score = ex_answers.first.score > 0.0 ? ex_answers.first.score : 0.0
        #     new_obj_score = ex_obj_score - ex_shixun_old_score + @c_score
        #     ex_answers.first.update_attribute("score", @c_score)
        #   else
        #     ex_shixun_option = {
        #         :exercise_question_id => exercise_question_id,
        #         :user_id => exercise_user.user_id,
        #         :exercise_shixun_challenge_id => @shixun_a_id,
        #         :score => @c_score,
        #         :status => 0
        #     }
        #     ExerciseShixunAnswer.create!(ex_shixun_option)
        #     new_obj_score = ex_obj_score + @c_score
        #   end
        #   total_scores = new_obj_score + ex_subj_score
        #   if total_scores < 0.0
        #     total_scores = 0.0
        #   elsif total_scores > ex_all_scores
        #     total_scores = ex_all_scores
        #   end
        #   ex_scores = {
        #       :objective_score => new_obj_score,
        #       :score => total_scores
        #   }
        #   exercise_user.update!(ex_scores)
        # elsif @exercise_question.question_type == Exercise::PROGRAM
        #   if exercise_answers_hash[exercise_user.user_id].exists?
        #     ex_pro_old_score = exercise_answers_hash[exercise_user.user_id].first.score > 0.0 ? exercise_answers_hash[exercise_user.user_id].first.score : 0.0
        #     new_obj_score = ex_obj_score - ex_pro_old_score + @c_score
        #     exercise_answers_hash[exercise_user.user_id].first.update_attribute("score", @c_score)
        #   else #如果学生未答，则创建新的答题记录
        #     answer_option = {
        #         :user_id => exercise_user.user_id,
        #         :exercise_question_id => exercise_question_id,
        #         :score => @c_score,
        #         :answer_text => ""
        #     }
        #     ExerciseAnswer.create!(answer_option)
        #     new_obj_score = ex_obj_score + @c_score
        #   end
        #
        #   total_scores = new_obj_score + ex_subj_score
        #   if total_scores < 0.0
        #     total_scores = 0.0
        #   elsif total_scores > ex_all_scores
        #     total_scores = ex_all_scores
        #   end
        #   ex_scores = {
        #       :objective_score => new_obj_score,
        #       :score => total_scores
        #   }
        #   exercise_user.update!(ex_scores)
        # end
      end
      fields.each do |d|
        exercise_user_update_sql += "#{d} = CASE id " + fields_values[d].join(" ")  + " END" + (d == 'reviewed' ? "" : ", ")
      end
      exercise_user_update_sql += " WHERE id IN (#{exercise_user_ids.join(',')})"
      ActiveRecord::Base.connection.execute(exercise_user_update_sql)
      Tiding.bulk_insert(
          values: tiding_attrs
      )
      ExerciseAnswerComment.bulk_insert(
          values: exercise_answer_comment_attrs
      )
      ExerciseAnswer.bulk_insert(
          values: exercise_answer_attrs
      )
      # 由于在评阅过程中exercise_answer可能是新建的,而现在保存exercise_answer放在最后统一执行，所以循环中的exercise_answer有可能不存在
      # 所以需要在前面更新完成之后再次检查exercise_answer_comments中exercise_answer_id为空的记录进行更新
      # 需要拼装一个 exercise_user_id,exercise_question_id => exercise_answer_id
      exercise_answers = @exercise_question.exercise_answers.where(user_id: @exercise_users.pluck(:user_id)).group_by{|ea| ea.user_id}
      grouped_exercise_users = @exercise_users.group_by(&:user_id)
      update_exercise_answer_comment_sql = "update exercise_answer_comments set exercise_answer_id = case "
      exercise_answers.each do |user_id, sub_exercise_answers|
        update_exercise_answer_comment_sql += " when exercise_user_id = #{grouped_exercise_users[user_id].first.id} and exercise_question_id = #{@exercise_question.id} then #{sub_exercise_answers[0].id} "
      end
      update_exercise_answer_comment_sql += " end where exercise_question_id = #{@exercise_question.id} and exercise_answer_id is null"
      ActiveRecord::Base.connection.execute(update_exercise_answer_comment_sql)

      normal_status("操作成功")
    end
  end

  def update_question_score
    tip_exception(-1, "分值不允许为空！") if params[:question_score].blank? && params[:question_scores].blank? #分值的数组或参数必需存在一个
    @exercise_question.update!(question_score: params[:question_score])
    normal_status("更新成功")
  end

  private

  def questions_params
    params.require(:exercise_question).permit(:question_title, :question_type,
                                              :question_number, :exercise_id,
                                              :question_score, :shixun_id, :is_ordered)
  end

  def get_exercise
    @exercise = Exercise.find_by(id: params[:exercise_id])
    if @exercise.blank?
      tip_exception(404, "试卷不存在")
    else
      @course = @exercise.course
      if @course.blank?
        tip_exception(404, "课堂不存在")
      end
    end
  end

  def is_course_teacher
    @identity = current_user.course_identity(@course)
    unless @identity < Course::STUDENT #为老师/助教/管理员
      tip_exception("403", "无权限操作")
    end
  end

  def get_exercise_question
    @exercise_question = ExerciseQuestion.find_by(id: params[:id])
    if @exercise_question.present?
      @exercise = @exercise_question.exercise
      if @exercise.blank?
        tip_exception(404, "试卷不存在")
      else
        @course = @exercise.course
        if @course.blank?
          tip_exception(404, "课堂不存在")
        end
      end
    else
      tip_exception(404, "试卷问题不存在")
    end
  end

  def validate_params
    normal_status(-1, "题目不允许为空！") if (params[:question_title].blank? && params[:question_type].to_i != Exercise::PRACTICAL) #除了实训题，其余题目必需有题干
    normal_status(-1, "问题类型不允许为空！") if params[:question_type].blank?
    normal_status(-1, "分值不允许为空！") if params[:question_score].blank? && params[:question_scores].blank? #分值的数组或参数必需存在一个
    if params[:question_score].present? && params[:question_score].to_f <= 0.0 #问题类型存在，则分值不能为空,且必需大于0
      normal_status(-1, "分值必需大于0！")
    elsif (params[:question_score].present? && params[:question_score].to_f.round(1) > 100.0) || (params[:question_scores].present? && (params[:question_scores].map {|a| a.to_f.round(1)}.max > 100.0))
      normal_status(-1, "分值不能超过100分！")
    elsif params[:question_scores].present? && params[:question_scores].include?(0.0) #如果有负数，则自动取绝对值，#多个分数值，针对实训题的
      normal_status(-1, "分值必需大于0！")
    elsif params[:standard_answers].present? && params[:question_choices].present? && (params[:standard_answers].count > params[:question_choices].count)
      normal_status(-1, "标准答案数不能大于选项数！")
    elsif [0, 1, 2, 3].include?(params[:question_type].to_i) && (params[:standard_answers].blank? || params[:standard_answers].include?("")) #选择题/判断题/填空题 问题选项/标准答案不能为空,也不能包含空的内容
      normal_status(-1, "标准答案不能为空！")
    elsif params[:question_type].to_i == 2 && (params[:standard_answers].count > 1 || params[:question_choices].blank? || params[:question_choices].include?("")) #判断题的标准答案不能大于1个，选项不能为空
      normal_status(-1, "判断题选项不能为空/标准答案不能大于1个！")
    elsif params[:question_type].to_i <= 1 && (params[:question_choices].blank? || params[:question_choices].include?("") || params[:question_choices].count < 2) #选择题选项不能为空,且不能小于2
      normal_status(-1, "选择题选项内容不能为空，且不能少于2个！")
    elsif params[:question_type].to_i == 3 && (params[:standard_answers].blank? || params[:standard_answers].count > 5) #填空题选项最多为5个,且如果为1个的话，不允许修改is_ordered
      normal_status(-1, "填空题标准答案不能为空/不能超过5个！")
    elsif params[:question_type].to_i == 4 && params[:standard_answers].count > 2 #简单题参考答案最多为1个
      normal_status(-1, "简答题的参考答案不能大于2个！")
    elsif params[:question_type].to_i == 5
      if params[:shixun_id].blank? #实训题的id不能为空
        normal_status(-1, "实训题id不能为空！")
      elsif params[:shixun_name].blank?
        normal_status(-1, "实训题名称不能为空！")
      end
    elsif params[:question_type].to_i == 6
      normal_status(-1, "编程题描述不能为空！") if params[:description].blank?
    end
  end

  def check_exercise_status
    normal_status(-1, "不能更改试卷问题！") if @exercise.exercise_status != Exercise::UNPUBLISHED
  end

  #更新时不能修改的内容
  def cannot_change_column
    #已发布的/已截止的/评阅中的状态时,不能修改分数，不能增删问题和答案,不能修改标准答案，可以修改选项内容/题目内容，这里仅指单个问题
    if @exercise.exercise_status != Exercise::UNPUBLISHED
      question_score = @exercise_question.question_score #原来的分数
      update_question_score = params[:question_score].to_f.round(1) #传入的分数
      choices_count = @exercise_question.exercise_choices.size #原来的选项个数
      exercise_choice_ids = @exercise_question.exercise_standard_answers.pluck(:exercise_choice_id).uniq
      standard_answers_text = @exercise_question.exercise_standard_answers.pluck(:answer_text).uniq
      update_choices_count = params[:question_choices].present? ? params[:question_choices].count : choices_count #传入的选项个数
      standard_answer = params[:standard_answers] #传参数是怎么传的？能不能传空值？
      if update_question_score.present? && question_score != update_question_score #分数有更改
        normal_status(-1, "已发布/已截止，分数不允许修改！")
      elsif update_choices_count != choices_count #选项个数有修改
        normal_status(-1, "已发布/已截止，不允许增删答案！")
      elsif standard_answer.present?
        if @exercise_question.question_type == Exercise::COMPLETION
          # exercise_answers_text = standard_answer.map{|a| a[:answer_text]}.sum.uniq
          # unless (standard_answer.count == exercise_choice_ids.count) && (standard_answers_text.count == exercise_answers_text.count)
          unless standard_answer.count == exercise_choice_ids.count
            normal_status(-1, "已发布/已截止，不允许增删标准答案！")
          end
        elsif @exercise_question.question_type == Exercise::SUBJECTIVE
          unless standard_answers_text.count == standard_answer.count
            normal_status(-1, "已发布/已截止，不允许增删标准答案！")
          end
        end
      end
    end
  end

  # 不论是批量评阅还是单独评阅都需要校验的逻辑
  def check_before_common_adjust_score
    @c_score = params[:score].to_f #调分后的分数
    if @exercise_question.question_type == Exercise::SINGLE || @exercise_question.question_type == Exercise::JUDGMENT
      normal_status(-1, "单选题/判断题不能调分!")
    elsif params[:comment].present? && params[:comment].length > 100
      normal_status(-1, "评语不能超过100个字符!")
    else
      @shixun_a_id = params[:shixun_challenge_id]
      if @exercise_question.question_type == Exercise::PRACTICAL #当为实训题时,为关卡的分数
        @shixun_challenge = @exercise_question.exercise_shixun_challenges.cha_id_find(@shixun_a_id)
        if @shixun_challenge.present?
          @old_ques_score = @shixun_challenge.first.question_score
        else
          @old_ques_score = nil
          normal_status(-1, "实训答案id不能为空!")
        end
      else
        @old_ques_score = @exercise_question.question_score #当不为实训题时，为各问题的分数
      end
      if @old_ques_score.present? && (@c_score > @old_ques_score)
        normal_status(-1, "分数不能大于题目分数!")
      end
    end
  end

  def check_before_adjust_score
    @c_score = params[:score].to_f #调分后的分数
    @user_id = params[:user_id]
    @exercise_current_user = @exercise.exercise_users.exercise_commit_users(@user_id).first #当前试卷用户的答案内容
    if @exercise_current_user.blank?
      normal_status(-1, "用户不存在！")
    elsif @c_score.blank?
      normal_status(-1, "分数不能为空!")
    end
  end

  def check_before_batch_adjust_score
    tip_exception(-1, "随机试卷暂不支持批量评阅") if @exercise.is_random?
    @exercise_users = @exercise.exercise_users.where(user_id: params[:user_ids]) if params[:user_ids].present? #批量评阅时,前端传过来的是id数组
    @min_score = params[:min_score]
    @max_score = params[:max_score]
    if @exercise_users.blank?
      normal_status(-1, "用户不存在！")
    elsif @min_score.blank? || @max_score.blank?
      normal_status(-1, "分数不能为空!")
    end
  end

  def check_exercise_users
    tip_exception(-1, "批量评阅仅支持对评阅中、未评阅的学生进行") if @exercise_users.exists?(reviewed: true)
    tip_exception(-1, "不支持对未交卷/考试中的学生进行评阅") if @exercise_users.exists?(commit_status: "none_commmit")
  end
end
