module Gaku
  class Exam < ActiveRecord::Base
    include Gradable
    include Pagination
    include Notes

    has_many :exam_scores
    has_many :exam_portions, -> { order :position }
    has_many :exam_portion_scores, through: :exam_portions

    has_many :exam_syllabuses, dependent: :destroy
    has_many :syllabuses, through: :exam_syllabuses

    has_many :attendances, as: :attendancable

    has_many :exam_sessions

    belongs_to :grading_method, required: false
    belongs_to :department, required: false

    validates :name, presence: true

    validates :weight, numericality: { allow_blank: true, greater_than_or_equal_to: 0 }

    accepts_nested_attributes_for :exam_portions

    def to_s
      name
    end

    def self.without_syllabuses
      includes(:syllabuses).where(standalone: false)
                           .select { |p| p.syllabuses.empty? }
    end

    def total_weight
      exam_portions.reduce(0) { |sum, p| p.weight ? sum + p.weight : sum }
    end

    def total_weight_except(portion)
      exam_portions.reduce(0) do |sum, p|
        if portion == p
          sum
        else
          p.weight ? sum + p.weight : sum
        end
      end
    end

    def max_score
      exam_portions.inject(0) { |sum, p| sum + p.max_score }
    end

    def completion(students)
      total_records = total_records(students)
      completion_ratio = 1 - (ungraded(students) / total_records.to_f)
      (completion_ratio * 100).round(2)
    end

    def ungraded(students)
      ungraded = 0

      students.each do |student|
        student_exam_eps = exam_portion_scores.select do |eps|
          eps.student_id == student.id
        end

        student_exam_eps.each do |eps|
          ungraded += 1 if check_record_completion?(eps)
        end
      end

      ungraded
    end

    def total_records(students)
      exam_portions.count * students.count
    end

    def completed_by_students(students)
      completed = []
      students.each do |student|
        completed.append(student.id) if completed_by_student?(student)
      end
      completed
    end

    def completed_by_student?(student)
      state = true

      exam_portions.each do |ep|
        student_eps = ep.exam_portion_scores.find do |eps|
          eps.student_id == student.id
        end
        state = false if check_record_completion?(student_eps)
      end

      state
    end

    def check_record_completion?(student_eps)
      student_eps.score.nil? && !student_eps.attendances
                                            .last
                                            .try(:attendance_type)
                                            .try(:auto_credit)
    end
  end
end
