def match_start_only?
  Card.config.search_box_match_start_only
end

format do
  delegate :match_start_only?, to: :card

  view :search_error, cache: :never do
    # don't show card content; not very helpful in this case
    %(#{search_with_params.class} :: #{search_with_params.message})
  end

  def search_with_params
    @search_with_params ||= cql_keyword? ? cql_search : super
  end

  def cql_search
    query = card.parse_json_cql search_keyword
    rescuing_bad_query(query) { Card.search query }
  end

  def cql_keyword?
    search_keyword&.match?(/^\{.+\}$/)
  end

  def complete_path
    path mark: :search, view: :search_box_complete, format: :json
    # path mark: :search, view: :test, format: :json
  end

  def results_path keyword
    path mark: :search, query: { keyword: keyword }
  end
end

format :html do
  view :search_box, template: :haml, cache: :never

  view :title do
    voo.title ||= t(:search_results_title)
    super()
  end

  view :core, cache: :never do
    [render_results_for_keyword, super()]
  end

  view :results_for_keyword, cache: :never, template: :haml

  def search_item term
    autocomplete_item :search, icon_tag(:search), term
  end

  private

  def search_form &block
    form_tag path, method: "get", role: "search", class: classy("search-box-form"), &block
  end

  def search_box_contents
    text_field_tag "query[keyword]", search_keyword,
                   class: "_search-box #{classy 'search-box'} form-control w-100",
                   autocomplete: :off,
                   data: { completepath: complete_path },
                   placeholder: t(:search_search_box_placeholder)
  end
end

format :json do
  view :search_box_complete, cache: :never, perms: :none do
    search_box_items :search_item, :add_item, :goto_items
  end

  view :complete, cache: :never, perms: :none do
    complete_or_match_search(start_only: match_start_only?).map do |name|
      goto_item_label name
    end
  end

  private

  def search_box_items *methods
    [].tap do |items|
      each_search_box_item methods do |action, value, label, data|
        items << data.merge(action: action, value: value, label: label)
      end
    end
  end

  def each_search_box_item methods, &block
    term = search_keyword
    exact = Card.fetch term, new: {}
    methods.map { |method| send method, term, exact, &block }
  end

  def search_item term, _exact
    yield :search, term, card.format.search_item(term), url: card_url(results_path(term))
  end

  def add_item term, exact
    return unless exact.add_autocomplete_ok?

    fmt = exact.format
    yield :add, term, fmt.render_add_autocomplete_item,
          url: card_url(fmt.add_autocomplete_item_path)
  end

  def goto_items _term, exact
    map_goto_items exact do |item|
      yield :goto, item, goto_item_label(item), url: path(mark: item)
    end
  end

  def goto_item_label item
    item.card.format.render_goto_autocomplete_item
  end

  def map_goto_items exact, &block
    goto_names = complete_or_match_search start_only: match_start_only?
    goto_names.unshift exact.name if go_to_exact_match? exact
    goto_names.uniq.map(&block)
  end

  def go_to_exact_match? exact
    exact.known?
  end

  def term_param
    # return nil unless query_params.present?
    #
    # term = query_params[:keyword]
    return unless (term = super)&.present?

    if (term =~ /^\+/) && (main = params["main"])
      term = main + term
    end
    term
  end
end
