class HomeworkCommon < ApplicationRecord
  # homework_type 1：普通作业  2：编程作业（弃用） 3：分组作业  4：实训作业
  enum homework_type: { normal: 1, program: 2, group: 3, practice: 4 }, _suffix: true
  has_many :homework_group_settings, dependent: :destroy
  has_many :published_settings, -> { group_published }, class_name: "HomeworkGroupSetting"
  has_many :student_works, -> { where(is_delete: 0) }
  has_many :score_student_works, -> { where("is_delete = 0 and work_status != 0") }, class_name: "StudentWork"
  has_one :homework_detail_manual, dependent: :destroy
  has_many :student_works_scores

  # 分组作业的设置
  has_one :homework_detail_group, dependent: :destroy

  belongs_to :course, counter_cache: true
  belongs_to :homework_bank, optional: true
  belongs_to :user
  has_many :homework_challenge_settings, dependent: :destroy

  has_one :homework_commons_shixun, dependent: :destroy
  has_and_belongs_to_many :shixuns, through: :homework_commons_shixun

  # attachtype: 1（描述的附件）， 2（参考答案的附件）
  has_many :attachments, as: :container, dependent: :destroy

  # 作业的评论
  has_many :journals_for_messages, as: :jour, dependent: :destroy

  # 二级目录
  belongs_to :course_second_category, optional: true

  # 课堂动态
  has_one :course_act, class_name: 'CourseActivity', as: :course_act, dependent: :destroy
  has_many :tidings, as: :container, dependent: :destroy
  # 实训作业的分班查重记录
  has_many :homework_group_reviews, :dependent => :destroy
  # 学生的查重情况
  has_many :homework_review_results, :dependent => :destroy

  validates :name, presence: true, length: { maximum: 500, too_long: "不能超过500个字符" }
  validates :description, length: { maximum: 66000, too_long: "不能超过66000个字符" }
  validates :explanation, length: { maximum: 5000, too_long: "不能超过5000个字符" }
  validates :reference_answer, length: { maximum: 66000, too_long: "不能超过66000个字符" }

  # after_update :update_activity
  before_destroy :update_homework_bank_quotes

  #1已发布的课堂作业
  scope :homework_published, -> {where("homework_commons.publish_time IS NOT NULL AND homework_commons.publish_time <= ? ",Time.now)}
  scope :published_no_end, -> {where("homework_commons.publish_time IS NOT NULL AND homework_commons.publish_time < ?
                                      and homework_commons.end_time > ?", Time.now, Time.now)}
  scope :homework_ended, -> {where("homework_commons.end_time IS NOT NULL AND homework_commons.end_time <= ? ",Time.now)}
  scope :search_homework_type, lambda {|num| where(homework_type:num)}
  scope :unified_setting, -> {where("unified_setting = ? ", 1)}

  scope :normals,  -> {where(homework_type: %i[normal]).order("position desc")}
  scope :groups,  -> {where(homework_type: %i[group]).order("position desc")}
  scope :practices,  -> {where(homework_type: %i[practice]).order("position desc")}

  # 是否显示参考答案
  def view_answer identity, user_id
    identity < Course::STUDENT || (identity == Course::STUDENT && answer_public &&
        self.end_or_late && self.user_work(user_id).try(:work_status).to_i > 0)
  end

  # 申诉阶段
  def appeal_duration
    self.anonymous_appeal && [3, 4].include?(homework_detail_manual.comment_status)
  end

  # 作业对应的子目录/父目录名称
  def category_info
    if self.course_second_category.present?
      {category_id: self.course_second_category.try(:id), category_name: self.course_second_category.try(:name), main: 0}
    else
      course_module = case homework_type
                      when 'normal'
                        course.common_course_modules.take
                      when 'group'
                        course.group_course_modules.take
                      when 'practice'
                        course.shixun_course_modules.take
                      end
      {category_id: course_module.try(:id), category_name: course_module.try(:module_name), main: 1}
    end
  end

  # 根据是否统一发布获取作业的作品列表
  def all_works
    student_works = self.unified_setting ? self.student_works :
                        self.student_works.joins("join course_members on student_works.user_id=course_members.user_id").
                            where(course_members: {course_group_id: self.published_settings.pluck(:course_group_id)})
  end

  # 分班权限的老师可见的作品列表
  def teacher_works member
    # member = course.course_member(user_id)
    teacher_course_groups = member.try(:teacher_course_groups)
    all_student_works = self.all_works
    # 有分班权限的统计管理的分班且已发布的学生情况
    if member.present? && teacher_course_groups.exists?
      group_ids = teacher_course_groups.pluck(:course_group_id)
      all_student_works = all_student_works.joins("join course_members on student_works.user_id=course_members.user_id").
          where(course_members: {course_group_id: group_ids})
    end
    all_student_works
  end

  def user_work user_id
    work = StudentWork.find_by(homework_common_id: id, user_id: user_id) || StudentWork.create!(homework_common_id: id, user_id: user_id)
  end

  # 是否在补交阶段内
  def late_duration
    homework_setting = self.homework_group_setting(User.current.id, true)
    !course.is_end && self.publish_time && self.publish_time < Time.now && homework_setting.end_time &&
        homework_setting.end_time < Time.now && self.allow_late && (self.late_time.nil? || self.late_time > Time.now)
  end

  # 作业是否补交截止或者不允许补交且提交截止
  def end_or_late
    status = false
    if self.course.is_end || (self.allow_late && self.late_time && self.late_time < Time.now)
      status = true
    elsif !self.allow_late
      homework_setting = self.homework_group_setting(User.current.id, true)
      status = homework_setting.end_time && homework_setting.end_time < Time.now
    end
    status
  end

  # 作业允许补交时取补交时间，否则取截止时间
  def end_ot_late_time
    allow_late ? late_time : end_time
  end

  # 作业是否补交截止或者不允许补交且提交截止(不考虑分班)
  def end_or_late_none_group
    course.is_end || (allow_late && late_time < Time.now) || (!allow_late && end_time < Time.now)
  end

  # 作业是否可以查重
  def code_review
    self.homework_type == 'practice' && self.publish_time.present? && self.publish_time < Time.now && self.homework_group_reviews.count == 0
  end

  # 作业查看最新成绩
  def update_score identity
    identity < Course::NORMAL && publish_time.present? && publish_time < Time.now && !end_or_late_none_group
  end

  # 作业能否立即发布
  def publish_immediately charge_ids
    homework_detail_manual.try(:comment_status) == 0 ||
      homework_group_settings.select{|setting| charge_ids.include?(setting.course_group_id) &&
        (setting.publish_time.nil? || setting.publish_time > Time.now)}.size > 0
  end

  # 作业能否立即截止
  def end_immediately charge_ids
    (unified_setting && homework_detail_manual.try(:comment_status) == 1 && end_time.present? && end_time > Time.now) ||
      homework_group_settings.select{|setting| charge_ids.include?(setting.course_group_id) &&
        !setting.publish_time.nil? && setting.publish_time < Time.now && setting.end_time > Time.now}.size > 0
  end

  # 学生是否提交了作品
  def user_has_commit_work user_id
    work = self.student_works.find_by_user_id(user_id)
    work.present? && work.work_status != 0
  end

  # 是否有学生提交过作品
  def has_commit_work
    student_works.has_committed.count > 0
  end

  # 是否有学生关联了项目
  def has_relate_project
    homework_type == "group" && student_works.where("project_id != 0").count > 0
  end

  # 作业描述的附件
  def des_attachments
    attachments.where(attachtype: 1)
  end

  # 参考答案的附件
  def ref_attachments
    attachments.where(attachtype: 2)
  end

  def update_activity
    course_activity = CourseActivity.find_by_course_act_type_and_course_act_id(self.class.to_s, self.id)

    course_activity.update_attributes(updated_at: Time.now) if course_activity
  end

  #删除时更新题库中的引用数
  def update_homework_bank_quotes
    old_bank = self.homework_bank
    old_bank.update_attributes(quotes: (old_bank.quotes - 1) > 0 ? (old_bank.quotes - 1) : 0, homework_common_id: nil) if old_bank.present?
  end

  # 查重是否有新结果
  def code_reviews_new_results?
    self.homework_group_reviews.where(status: 0).count > 0
  end

  # 任务已评数 user_id: 对应老师的id
  def comment_count(user_id)
    course = self.course
    # 如果有分班，则取此老师对应分班的已评数，没有分班，则取所有的
    course_group_ids = course.group_course_power(user_id)
    sql =
        if course_group_ids.count > 0
          %Q{
            SELECT count(distinct sw.id) cnt FROM student_works sw
              JOIN student_works_scores sws on sws.student_work_id = sw.id WHERE
              reviewer_role IN(1,2) and sw.is_delete = 0 AND sw.id IN (SELECT id FROM student_works WHERE homework_common_id = #{self.id}
              AND user_id IN (SELECT user_id FROM course_members WHERE role = 4 AND course_group_id
                    IN (#{course_group_ids.join(",")})
                   )
                )
          }
        else
          %Q{
            SELECT COUNT(distinct sw.id) cnt FROM student_works sw
              JOIN student_works_scores sws ON sw.id = sws.student_work_id
            WHERE reviewer_role IN(1,2) and sw.is_delete = 0 AND sw.homework_common_id = #{self.id}
          }
        end
    StudentWork.find_by_sql(sql).first.try(:cnt).to_i
  end

  # 任务未评数
  def uncomment_count(user_id)
    user_ids = course.teacher_group_user_ids user_id
    self.student_works.where(user_id: user_ids).has_committed.count - self.comment_count(user_id)
  end

  # 作品未提交数
  def unfinished_count member
    self.teacher_works(member).unfinished.count
  end

  # 任务按时提交数
  def finished_count member
    self.teacher_works(member).finished.count
  end

  def delay_finished_count member
    self.teacher_works(member).delay_finished.count
  end

  def compelete_status_count member, status
    teacher_works(member).where(compelete_status: status).count
  end

  # 分组作业的最大分组id
  def max_group_id
    self.student_works.has_committed.maximum(:group_id).to_i + 1
  end

  # 作业的分班设置时间
  def homework_group_setting user_id, current_user=false
    if unified_setting
      homework_setting = self
    else
      # 当前用户是从course_member中取，否则是从学生中取（双重身份的原因）
      member = current_user ? course.course_member(user_id) : course.students.find_by(user_id: user_id)
      group_setting = self.homework_group_settings.select{ |setting| setting.course_group_id == member.try(:course_group_id)}.first
      homework_setting = group_setting.present? ? group_setting : self
    end
    homework_setting
  end

  def min_group_publish_time
    #HomeworkGroupSetting.where("homework_common_id = #{self.id} and publish_time is not null").pluck(:publish_time).min
    # 可以使用includes
    self.homework_group_settings.where("publish_time is not null").pluck(:publish_time).min
  end

  def max_group_end_time
    #HomeworkGroupSetting.where("homework_common_id = #{self.id} and end_time is not null").pluck(:end_time).max
    # 可以使用includes
    self.homework_group_settings.where("end_time is not null").pluck(:end_time).max

  end

  def challenge_score challenge_id
    homework_challenge_settings.find_by(challenge_id: challenge_id)&.score.to_f
  end

  def create_homework_group_settings
    if homework_group_settings.size != course.course_groups.size
      course.course_groups.where.not(id: homework_group_settings.pluck(:course_group_id)).each do |group|
        homework_group_settings << HomeworkGroupSetting.new(course_group_id: group.id, course_id: course.id,
                                                                     publish_time: publish_time, end_time: end_time)
      end
    end
  end

  def update_homework_work_score
    ActiveRecord::Base.transaction do
      if unified_setting
        user_ids = course.students.pluck(:user_id)
      else
        user_ids = course.students.where(course_group_id: published_settings.pluck(:course_group_id)).pluck(:user_id)
      end

      # 作业关卡设置
      challenge_setting_ids = []
      challenge_settings = {}

      # 一次查询出所有关卡设置数据，避免多次查询
      homework_challenge_settings.each do |d|
        challenge_settings[d.challenge_id] = d.score
        challenge_setting_ids << d.challenge_id
      end

      myshixuns = Myshixun.joins("JOIN student_works ON myshixuns.user_id = student_works.user_id")
                          .where("shixun_id = ? AND myshixuns.user_id in (?) AND student_works.homework_common_id = #{self.id}", homework_commons_shixun&.shixun_id, user_ids)
                          .select("student_works.id AS student_work_id, work_status, final_score, eff_score, efficiency, commit_time, myshixun_id,
                                  update_time, compelete_status, cost_time, late_penalty, ultimate_score, calculation_time, work_score, myshixuns.*")

      # 一次加载所有的用户关卡分数数据，避免多次查询
      challenge_work_scores = ChallengeWorkScore.joins("JOIN student_works ON challenge_work_scores.student_work_id = student_works.id").where("student_works.user_id in (?)", user_ids).select("student_work_id, challenge_id, score")
      
      # 分批加载实训作业数据，batch_size的大小根据当前机器环境来设置适中的数值，过大或过小，处理速度都会有影响
      myshixuns.find_in_batches(batch_size: 400) do |group|
        # 组装sql，分批次更新实训成绩
        update_sql = "UPDATE student_works SET "
        # 批量更新 student_works 的相关字段
        fields = %W(compelete_status work_status efficiency late_penalty commit_time myshixun_id update_time final_score work_score calculation_time)
        field_values = {}
        fields.each { |d| field_values[d] = []}
        student_works_ids = []

        group.each do |myshixun|
          student_works_ids << myshixun['student_work_id']
          games = myshixun.games.joins(:challenge).where("games.challenge_id IN (?)", challenge_setting_ids)
                                .select("games.end_time, games.status, games.final_score, challenges.score AS challenge_score, games.cost_time, games.challenge_id")

          student_work = HomeworksService.new.batch_update_myshixun_work_score myshixun, games, self, challenge_settings, challenge_work_scores
          fields.each do |d|
            field_values[d] << " WHEN #{myshixun['student_work_id']} THEN #{student_work[d.to_sym] || 'null'} "
          end
        end

        fields.each do |d|
          update_sql += "#{d} = CASE id " + field_values[d].join(" ")  + " END" + (d == 'calculation_time' ? "" : ", ")
        end
        update_sql += " WHERE id IN (#{student_works_ids.join(',')})"
        ActiveRecord::Base.connection.execute(update_sql)
      end
      HomeworksService.new.update_student_eff_score(self) if (allow_late && late_time < Time.now) || (!allow_late && end_time < Time.now)
      update_attribute('calculation_time', Time.now)
    end
  end
end
