class Admin::Admissions::StudentsController < Admin::Admissions::Controller
  def index
    data = students
      .preload(:family)
      .join_school_year_student(params[:school_year_id], params[:status].to_sym)
      .by_applicant(current_school, params[:is_applicant]&.to_bool)
      .distinct
      .ordered

    render_success :ok, json: data.map { |s| student_props(s) }
  end

  def create_applicant
    if student.school_year_students.exists?
      applicant = student.admission_applicants.build(applicant_params.merge(returning: true))
    else
      applicant = current_school.admission_applicants.build(applicant_params)
      applicant.student = student
    end

    if applicant.save
      render_success :ok, message: 'Applicant created.'
    else
      render_error :unprocessable_entity, errors: applicant
    end
  end

  def enroll
    # skip enrolling student if a year already exists
    if school_year_student.id || school_year_student.update(current: true, grade_id: params[:grade])
      render_success :ok, event: :enrolled
    else
      render_error :unprocessable_entity, errors: school_year_student
    end
  end

  def batch_create_applicants
    data = []
    args = { state: params[:state], review: params[:review] }
    non_applicant_students.each do |student|
      has_enrollment_records = students_without_enrollment.exclude?(student.id)
      applicant_params = args.merge(
        family_id: student.family_id,
        grade: has_enrollment_records ? student.grade + 1 : student.grade,
        returning: has_enrollment_records
      )

      if has_enrollment_records
        applicant = student.admission_applicants.build(applicant_params)
      else
        applicant = current_school.admission_applicants.build(applicant_params)
        applicant.student = student
      end
      next if applicant.application_id.nil?

      data << applicant
    end

    if transactional_save(data)
      render_success :ok, message: 'Applicants created.'
    else
      render_error :unprocessable_entity
    end
  end

  def batch_enroll
    data = []
    non_applicant_students.each do |student|
      # skip enrolling student if a year already exists
      next if school_year_students[student.id]

      has_enrollment_records = students_without_enrollment.exclude?(student.id)
      school_year_student = student.school_year_students.build(
        school_year: current_admission_year,
        current: true,
        grade_id: has_enrollment_records ? student.grade + 1 : student.grade
      )
      data << school_year_student
    end

    if transactional_save(data, true)
      render_success :ok, message: 'Students enrolled.'
    else
      render_error :unprocessable_entity
    end
  end

  def list_pdf
    Reporting::Admissions::StudentJob.perform_async(
      current_school.id,
      current_user.id,
      school_year_id: params[:school_year_id],
      is_applicant: params[:is_applicant],
      status: params[:status]
    )
  end

  private
    def students
      current_school.students
    end

    def student
      @student ||= students.find_by(id: params[:id])
    end

    def enrollment_by_student
      @enrollment_by_student ||= school_year.school_year_students.index_by(&:student_id)
    end

    def non_applicant_students
      students.by_applicant(current_school, false).where(id: params[:ids])
    end

    def school_year_students
      @school_year_students ||= current_admission_year.school_year_students
        .where(student_id: params[:ids])
        .index_by(&:student_id)
    end

    def school_year_student
      @school_year_student ||= student.school_year_students
        .find_or_initialize_by(school_year: current_admission_year)
    end

    def applicants_by_student_id
      @applicants_by_student_id ||= current_school.admission_applicants
        .where(admission_applications: { school_year_id: current_admission_year })
        .order('admission_applications.enrollment DESC')
        .group_by(&:student_id)
    end

    def school_year
      @school_year ||= current_school.school_years.find_by(id: params[:school_year_id]) ||
        current_school_year
    end

    def current_student_ids
      @current_student_ids ||= school_year.school_year_students
        .where(current: true)
        .pluck(:student_id)
    end

    def students_without_enrollment
      @students_without_enrollment ||= current_school.students
        .left_joins(:school_year_students)
        .where(SchoolYearStudents: { SchoolYearStudentID: nil })
        .pluck(:id)
    end

    def students_enrolled_in_admission_year
      @students_enrolled_in_admission_year ||= current_admission_year.school_year_students
        .where(current: true)
        .pluck(:student_id)
    end

    def applicant_params
      params.permit(:grade, :state, :review).merge(family: student.family)
    end

    def student_props(student)
      enrollment_grade = enrollment_by_student[student.id]&.grade_id || student.grade
      has_enrollment_records = students_without_enrollment.exclude?(student.id)
      applicant = applicants_by_student_id[student.id]&.first
      {
        id: student.id,
        full_name: student.full_name(:reverse),
        family_name: student.family.name,
        application_grade: has_enrollment_records ? student.grade + 1 : student.grade,
        grade_label: grade_levels[enrollment_grade],
        grade: enrollment_grade,
        is_applicant: applicant.present?,
        current: current_student_ids.include?(student.id),
        has_enrollment_records: has_enrollment_records,
        enrolled_in_admissions_year: students_enrolled_in_admission_year.include?(student.id),
        applicant_id: applicant&.id,
        applicant_review: applicant&.review,
        applicant_system_status: applicant&.decorate&.system_status
      }
    end
end
