class Admission::Application < ApplicationRecord
  include Castable

  audited

  cast_as_editor_content :welcome_message
  cast_as_editor_content :completion_message

  belongs_to :school
  belongs_to :school_year

  has_many :applicants, dependent: :restrict_with_error
  has_many :application_grades, validate: true, dependent: :destroy, autosave: true
  has_many :application_agreements, dependent: :destroy
  has_many :application_attachments, dependent: :destroy
  has_many :application_checkboxes, dependent: :destroy
  has_many :application_documents, dependent: :destroy
  has_many :application_essays, dependent: :destroy
  has_many :profile_fields, dependent: :destroy, autosave: true
  has_many :displayable_profile_fields, -> { where(display: true) },
    class_name: 'Admission::ProfileField', inverse_of: :application
  has_many :required_profile_fields, -> { where(required: true) },
    class_name: 'Admission::ProfileField', inverse_of: :application
  has_many :additional_fields, dependent: :destroy

  has_many :agreements, through: :application_agreements
  has_many :attachments, through: :application_attachments
  has_many :checkboxes, through: :application_checkboxes
  has_many :documents, through: :application_documents
  has_many :essays, through: :application_essays
  has_many :families, through: :applicants, source: :family
  has_many :student_additional_fields, through: :additional_fields
  has_many :students, through: :applicants, source: :student

  has_one :application_email, autosave: true, dependent: :destroy

  delegate :grades_hash, to: :school

  before_save :remove_empty_html

  before_save do
    self.new_students = true unless enrollment
  end

  before_create -> { profile_fields.build_all(self) }, if: -> { profile_fields.blank? }

  validates :name, presence: true

  validate :at_least_one_available_grade

  accepts_nested_attributes_for :application_email

  scope :by_open, ->(flag) { where(open: flag) unless flag.nil? }

  scope :by_returning, ->(flag) do
    return if flag.nil?

    flag ? where(returning_students: true) : where(new_students: true)
  end

  scope :by_school_year, ->(school_year_id) do
    where(school_year_id: school_year_id) if school_year_id
  end

  scope :by_enrollment, ->(flag) do
    where(enrollment: flag) unless flag.nil?
  end

  scope :for_grade, ->(grade) do
    if grade
      joins(:application_grades)
        .where(admission_application_grades: { grade: grade, available: true })
    end
  end

  def applicants_label
    if !enrollment
      'New Students'
    elsif new_students && returning_students
      'New & Returning Students'
    elsif new_students
      'New Students'
    elsif returning_students
      'Returning Students'
    else
      ''
    end
  end

  def grades_label
    ranges = []
    range = []
    application_grades.where(available: true).order(:grade).each do |grade|
      if range.empty? || (grade.grade - range.last) == 1
        range << grade.grade
      else
        ranges << range
        range = [grade.grade]
      end
    end
    ranges << range unless range.empty?
    labels = []
    ranges.each do |item|
      labels << if item.length == 1
        grades_hash[item.first]
      else
        "#{grades_hash[item.first]}-#{grades_hash[item.last]}"
      end
    end
    labels.join(', ')
  end

  def siblings
    school_year.admission_applications.where.not(id: id)
  end

  def copy(school_year)
    application_props = dup.attributes.except!('school_year_id')
    new_application = school_year.admission_applications.build(application_props)
    copy_children_components(new_application)

    new_application
  end

  private
    def associations
      [
        :profile_fields,
        :additional_fields,
        :application_agreements,
        :application_essays,
        :application_documents,
        :application_checkboxes,
        :application_attachments
      ]
    end

    def copy_children_components(new_application)
      associations.each do |association|
        send(association).each do |component|
          props = component.dup.attributes.except!('application_id')
          new_application.send(association).build(props)
        end
      end
    end

    def at_least_one_available_grade
      return if application_grades.to_a.find(&:available)

      errors.add(:grades, 'must be selected')
    end

    def remove_empty_html
      self.welcome_message = nil if ActionView::Base.full_sanitizer.sanitize(welcome_message).blank?
    end
end
