class Admin::Admissions::ApplicationsController < Admin::Admissions::Controller
  def index
    @applications = applications
      .by_school_year(params[:school_year_id])
      .for_grade(params[:grade_id])
      .by_open(params[:open]&.to_bool)
      .by_enrollment(params[:enrollment]&.to_bool)
      .by_returning(params[:returning]&.to_bool)
      .order(:name)

    render_success :ok, json: @applications.map { |d| application_props(d) }
  end

  def show
    render_success :ok, json: application_props(application)
  end

  def create
    @application = applications.build(application_params)
    @application.school_year = school_year
    set_available_grades

    if @application.save
      render_success :ok, json: application_props(application)
    else
      render_error :unprocessable_entity, errors: @application
    end
  end

  def update
    application.school_year = school_year if school_year
    set_available_grades if params[:grades]

    if application.update(application_params)
      render_success :ok, json: application_props(application)
    else
      render_error :unprocessable_entity, errors: application
    end
  end

  def destroy
    if application.destroy
      render_success :ok
    else
      render_error :unprocessable_entity, errors: application
    end
  end

  def copy
    @application = application.copy(school_year)
    @application.open = false
    grade_levels.keys.each do |grade|
      available = params[:application][:grades].include?(grade)
      @application.application_grades.build(grade: grade, available: available)
    end

    if @application.save
      render_success :ok, json: { id: @application.id }
    else
      render_error :unprocessable_entity, errors: @application
    end
  end

  def available_grades
    unused_per_application = available_application_grades(false)
    used_per_application = available_application_grades(true)

    add_grades = unused_per_application + used_per_application
    minus_grades = unused_per_application - used_per_application
    unused_grades = grade_levels.keys - add_grades + minus_grades

    if clean_application?
      unused_grades += application.application_grades.where(available: true).pluck(:grade)
    end

    render_success :ok, json: unused_grades.uniq
  end

  private
    def applications
      current_school.admission_applications
    end

    def application
      @application ||= applications.find_by(id: params[:id])
    end

    def grades
      @grades ||= application.application_grades.build_all(application)
    end

    def application_email
      @application_email ||= Admission::ApplicationEmail
        .find_or_initialize_by(application: application)
    end

    def available_application_grades(available)
      current_school.admission_application_grades
        .where(admission_applications: {
          enrollment: params[:enrollment].to_bool,
          new_students: params[:new_students].to_bool,
          returning_students: params[:returning_students].to_bool,
          school_year_id: params[:school_year_id]
        })
        .where(available: available)
        .pluck(:grade)
    end

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

    def set_available_grades
      grades.each do |grade|
        value = params[:grades].find { |g| (g[:grade] || g[:id]) == grade.grade }
        next if value.nil?

        grade.available = value[:available] || false
      end
    end

    def applicant_count
      @applicant_count ||= current_school.admission_applicants.group(:application_id).count
    end

    def clean_application?
      return false if application.blank?
      return false if application.school_year_id != params[:school_year_id].to_i
      return false if application.enrollment != params[:enrollment]&.to_bool
      return false if application.new_students != params[:new_students]&.to_bool

      application.returning_students == params[:returning_students]&.to_bool
    end

    def grades_by_applications
      @grades_by_applications ||= current_school.admission_application_grades
        .where(application_id: @applications || application, available: true)
        .group_by(&:application_id)
    end

    def application_params
      params.require(:application).permit(
        :name,
        :open,
        :welcome_message,
        :completion_message,
        :require_medical,
        :enrollment,
        :new_students,
        :returning_students,
        :payment,
        :payment_description,
        :payment_amount,
        application_email_attributes: [:id, :email]
      )
    end

    def application_props(application)
      {}.tap do |props|
        props[:id] = application.id
        props[:name] = application.name
        props[:open] = application.open
        props[:type] = application.enrollment ? 'Enrollment' : 'Entrance'
        props[:enrollment] = application.enrollment
        props[:grade_labels] = application.grades_label
        props[:applicant_type] = application.applicants_label
        props[:applicant_counts] = applicant_count[application.id] || 0
        props[:require_medical] = application.require_medical
        props[:welcome_message] = application.welcome_message
        props[:completion_message] = application.completion_message
        props[:school_year_id] = application.school_year_id
        props[:new_students] = application.new_students
        props[:returning_students] = application.returning_students
        props[:application_email_attributes] = application_email_props
        props[:application_email_id] = application_email.id
        props[:email] = application_email.email
        props[:grades] = grades_by_applications[application.id]&.map(&:grade)
        props[:payment] = application.payment
        props[:payment_description] = application.payment_description
        props[:payment_amount] = application.payment_amount
      end
    end

    def application_email_props
      {
        id: application_email.id,
        email: application_email.email
      }
    end
end
