class ReportElement < ApplicationRecord
  enum type_of: Extends::REPORT_ELEMENT_TYPES

  # This is only used by certain elements
  enum sort_order: {
    asc: 0,
    desc: 1
  }

  validates :position, presence: true
  validates :report, presence: true
  validates :type_of, presence: true
  validate :has_one_of_referenced_elements

  belongs_to :report, inverse_of: :report_elements, optional: true

  # Hierarchical structure representation
  has_many :children,
           -> { order(:position) },
           class_name: 'ReportElement',
           foreign_key: 'parent_id',
           dependent: :destroy
  belongs_to :parent, class_name: 'ReportElement', optional: true

  # References to various report entities
  belongs_to :project, inverse_of: :report_elements, optional: true
  belongs_to :experiment, inverse_of: :report_elements, optional: true
  belongs_to :my_module, inverse_of: :report_elements, optional: true
  belongs_to :step, inverse_of: :report_elements, optional: true
  belongs_to :result, inverse_of: :report_elements, optional: true
  belongs_to :checklist, inverse_of: :report_elements, optional: true
  belongs_to :asset, inverse_of: :report_elements, optional: true
  belongs_to :table, inverse_of: :report_elements, optional: true
  belongs_to :repository, inverse_of: :report_elements, optional: true

  def has_children?
    children.length > 0
  end

  def result?
    result_asset? or result_table? or result_text?
  end

  def comments?
    step_comments? or result_comments?
  end

  # Get the referenced elements (previously, element's type_of must be set)
  def element_references
    ReportExtends::ELEMENT_REFERENCES.each do |el_ref|
      if el_ref.check(self)
        return el_ref.elements.map { |el| eval(el.gsub('_id', '')) }
      end
    end
  end

  # Set the element references (previously, element's type_of must be set)
  def set_element_references(ref_ids)
    ReportExtends::SET_ELEMENT_REFERENCES_LIST.each do |el_ref|
      check = el_ref.check(self)
      next unless check
      el_ref.elements.each do |element|
        public_send("#{element}=", ref_ids[element])
      end
      break
    end
  end

  # removes element that are archived or deleted
  def clean_removed_or_archived_elements
    parent_model = ''
    %w(project
       experiment
       my_module
       step
       result
       checklist
       asset
       table
       repository)
      .each do |el|
      parent_model = el if send el
    end

    if parent_model == 'experiment'
      destroy unless send(parent_model).project == report.project
    else
      destroy unless (send(parent_model).active? rescue send(parent_model))
    end
  end

  private

  def has_one_of_referenced_elements
    element_references.each do |el|
      next unless el.nil?
      errors.add(:base,
                 'Report element doesn\'t have correct element references.')
      break
    end
  end
end
