class NestedSelectInput < ActiveAdminAddons::InputBase
  def to_html
    for_each_level { |attribute, data| concat(builder.input(attribute, level_options(data))) }
    parts_to_html
  end

  private

  def for_each_level
    hierarchy.each do |level_data|
      yield(level_data.delete(:attribute), level_data)
    end
  end

  def hierarchy
    data = get_levels_data
    raise("Undefined levels on nested_select") if data.empty?
    set_parent_attributes(data)
    data.reverse
  end

  def get_levels_data
    levels_data = []
    levels = @options.keys.select { |key| key.to_s.match(/level_[1-9]$/) }.reverse

    levels.each_with_index do |level_key, idx|
      previous_level_key = levels[idx + 1]
      validate_level_data!(level_key, previous_level_key)
      levels_data << @options.delete(level_key)
    end

    levels_data
  end

  def validate_level_data!(level_key, previous_level_key)
    current_level_number = get_level_number(level_key)
    previous_level_number = get_level_number(previous_level_key)

    if current_level_number != (previous_level_number + 1)
      raise("Missing :level_#{current_level_number - 1} key")
    end

    attribute = @options[level_key][:attribute]
    raise("Missing mandatory attribute level_key on #{level_key}") unless attribute
  end

  def get_level_number(level_key)
    level_key.to_s.split("_").last.to_i
  end

  def level_options(data)
    @options.merge(data).merge(as: :nested_level)
  end

  def set_parent_attributes(hierarchy)
    hierarchy.each_with_index do |level_data, idx|
      next_idx = idx + 1
      parent_level_data = hierarchy[next_idx] if hierarchy.count != next_idx
      if parent_level_data
        level_data[:parent_attribute] = parent_level_data[:attribute]
        set_parent_value(level_data)
      end
    end
  end

  def set_parent_value(level_data)
    parent_attribute = level_data[:parent_attribute]
    build_virtual_attr(parent_attribute)
    instance = instance_from_level_data(level_data)
    if instance && instance.respond_to?(parent_attribute)
      @object.send("#{parent_attribute}=", instance.send(parent_attribute))
    end
  end

  def instance_from_level_data(level_data)
    return unless level_data
    attribute_key = level_data[:attribute]
    attribute_value = @object.send(level_data[:attribute])
    return unless attribute_value
    klass = if level_data[:multiple]
              attribute_key.to_s.chomp("_ids").camelize.singularize.constantize
            else
              attribute_key.to_s.chomp("_id").camelize.constantize
            end
    klass.find_by_id(attribute_value)
  end

  def build_virtual_attr(attribute_name)
    if !@object.respond_to?(attribute_name) && !@object.respond_to?("#{attribute_name}=")
      @object.singleton_class.send(:attr_accessor, attribute_name)
    end
  end
end
