class QueryResult

  include Enumerable

  attr_reader :klass, :response, :current_page, :per_page, :error, :notice

  def initialize(model_name, response, options={})
    @klass = model_name.classify.constantize
    @response = response
    @current_page = options[:page] || 1
    @per_page = options[:per_page] || ArchiveConfig.ITEMS_PER_PAGE
    @error = response[:error]
    @notice = max_search_results_notice
  end

  def hits
    response.dig('hits', 'hits')
  end

  def items
    return [] if response[:error]
    if @items.nil?
      @items = klass.load_from_elasticsearch(hits, scopes: @scopes)
    end
    @items
  end

  def scope(*args)
    @scopes ||= []
    @scopes += args
    @items = nil # reset the items in case we already loaded them
    self # for chaining
  end

  def each(&block)
    items.each(&block)
  end

  def empty?
    items.empty?
  end

  def size
    items.size
  end
  alias :length :size

  def [](index)
    items[index]
  end

  def to_ary
    items
  end

  def load_tag_facets(type, info)
    @facets[type] = []
    buckets = info["buckets"]
    ids = buckets.map { |result| result['key'] }
    tags = Tag.where(id: ids).group_by(&:id)
    buckets.each do |facet|
      unless tags[facet['key'].to_i].blank?
        @facets[type] << QueryFacet.new(facet['key'], tags[facet['key'].to_i].first.name, facet['doc_count'])
      end
    end
  end

  def load_collection_facets(info)
    @facets["collections"] = []
    buckets = info["buckets"]
    ids = buckets.map { |result| result['key'] }
    collections = Collection.where(id: ids).group_by(&:id)
    buckets.each do |facet|
      unless collections[facet['key'].to_i].blank?
        @facets["collections"] << QueryFacet.new(facet['key'], collections[facet['key'].to_i].first.title, facet['doc_count'])
      end
    end
  end

  def load_facets(aggregations)
    aggregations.each_pair do |term, results|
      if Tag::TYPES.include?(term.classify) || term == "tag"
        load_tag_facets(term, results)
      elsif term == "collections"
        load_collection_facets(results)
      elsif term == "bookmarks"
        load_facets(results["filtered_bookmarks"])
      elsif term == "bookmarkable"
        load_facets(results)
      end
    end
  end

  def facets
    return if response["aggregations"].nil?

    if @facets.nil?
      @facets = {}
      load_facets(response["aggregations"])
    end

    @facets
  end

  def total_pages
    (total_entries / per_page.to_f).ceil rescue 0
  end

  # For pagination / fetching results.
  def total_entries
    [unlimited_total_entries, ArchiveConfig.MAX_SEARCH_RESULTS].min
  end

  def unlimited_total_entries
    response.dig('hits', 'total', 'value') || 0
  end

  def offset
    (current_page * per_page) - per_page
  end

  def max_search_results_notice
    # if we're on the last page of search results AND there are more results than we can show
    return unless current_page >= total_pages && unlimited_total_entries > total_entries
    ActionController::Base.helpers.ts("Displaying %{displayed} results out of %{total}. Please use the filters or edit your search to customize this list further.",
                                      displayed: total_entries,
                                      total: unlimited_total_entries
                                   ).html_safe
  end
end
