# frozen_string_literal: true

RSpec.describe CatalogHelper do
  include ERB::Util
  include described_class

  def mock_response args
    current_page = args[:current_page] || 1
    per_page = args[:rows] || args[:per_page] || 10
    total = args[:total]
    start = (current_page - 1) * per_page

    mock_docs = (1..total).to_a.map { ActiveSupport::HashWithIndifferentAccess.new }

    mock_response = Kaminari.paginate_array(mock_docs).page(current_page).per(per_page)

    allow(mock_response).to receive(:docs).and_return(mock_docs.slice(start, per_page))
    mock_response
  end

  describe "page_entries_info" do
    it "with no results" do
      @response = mock_response total: 0

      html = page_entries_info(@response, entry_name: 'entry_name')
      expect(html).to eq "No entry_names found"
      expect(html).to be_html_safe
    end

    it "with no results (and no entry_name provided)" do
      @response = mock_response total: 0

      html = page_entries_info(@response)
      expect(html).to eq "No entries found"
      expect(html).to be_html_safe
    end

    context "when response.entry_name is nil" do
      it "does not raise an error" do
        collection = mock_response total: 10
        allow(collection).to receive(:entry_name).and_return(nil)

        expect { page_entries_info(collection) }.not_to raise_error
      end
    end

    describe "with a single result" do
      it "uses the provided entry name" do
        response = mock_response total: 1

        html = page_entries_info(response, entry_name: 'entry_name')
        expect(html).to eq "<strong>1</strong> entry_name found"
        expect(html).to be_html_safe
      end

      it "infers a name" do
        response = mock_response total: 1

        html = page_entries_info(response)
        expect(html).to eq "<strong>1</strong> entry found"
        expect(html).to be_html_safe
      end
    end

    it "with a single page of results" do
      response = mock_response total: 7

      html = page_entries_info(response, entry_name: 'entry_name')
      expect(html).to eq "<strong>1</strong> - <strong>7</strong> of <strong>7</strong>"
      expect(html).to be_html_safe
    end

    it "on the first page of multiple pages of results" do
      @response = mock_response total: 15, per_page: 10

      html = page_entries_info(@response, entry_name: 'entry_name')
      expect(html).to eq "<strong>1</strong> - <strong>10</strong> of <strong>15</strong>"
      expect(html).to be_html_safe
    end

    it "on the second page of multiple pages of results" do
      @response = mock_response total: 47, per_page: 10, current_page: 2

      html = page_entries_info(@response, entry_name: 'entry_name')
      expect(html).to eq "<strong>11</strong> - <strong>20</strong> of <strong>47</strong>"
      expect(html).to be_html_safe
    end

    it "on the last page of results" do
      @response = mock_response total: 47, per_page: 10, current_page: 5

      html = page_entries_info(@response, entry_name: 'entry_name')
      expect(html).to eq "<strong>41</strong> - <strong>47</strong> of <strong>47</strong>"
      expect(html).to be_html_safe
    end

    it "works with rows the same as per_page" do
      @response = mock_response total: 47, rows: 20, current_page: 2

      html = page_entries_info(@response, entry_name: 'entry_name')
      expect(html).to eq "<strong>21</strong> - <strong>40</strong> of <strong>47</strong>"
      expect(html).to be_html_safe
    end

    it "uses delimiters with large numbers" do
      @response = mock_response total: 5000, rows: 10, current_page: 101
      html = page_entries_info(@response, entry_name: 'entry_name')

      expect(html).to eq "<strong>1,001</strong> - <strong>1,010</strong> of <strong>5,000</strong>"
    end

    context "with an ActiveRecord collection" do
      subject { helper.page_entries_info(Bookmark.page(1).per(25)) }

      let(:user) { User.create! email: 'xyz@example.com', password: 'xyz12345' }

      before { 50.times { Bookmark.create!(user: user) } }

      it { is_expected.to eq "<strong>1</strong> - <strong>25</strong> of <strong>50</strong>" }
    end
  end

  describe "rss_feed_link_tag" do
    context "when an alternate scope is passed in" do
      subject(:tag) { helper.rss_feed_link_tag(route_set: my_engine) }

      let(:my_engine) { double("Engine") }
      let(:query_params) { { controller: 'catalog', action: 'index' } }
      let(:config) { Blacklight::Configuration.new }
      let(:search_state) { Blacklight::SearchState.new(query_params, config, controller) }

      before do
        allow(helper).to receive(:search_state).and_return search_state
      end

      it "calls url_for on the engine scope" do
        expect(my_engine).to receive(:url_for).and_return('/rss-path')
        expect(tag).to match /title="RSS for results"/
        expect(tag).to match /rel="alternate"/
        expect(tag).to match %r{type="application/rss\+xml"}
      end
    end
  end

  describe "atom_feed_link_tag" do
    context "when an alternate scope is passed in" do
      subject(:tag) { helper.atom_feed_link_tag(route_set: my_engine) }

      let(:my_engine) { double("Engine") }
      let(:query_params) { { controller: 'catalog', action: 'index' } }
      let(:config) { Blacklight::Configuration.new }
      let(:search_state) { Blacklight::SearchState.new(query_params, config, controller) }

      before do
        allow(helper).to receive(:search_state).and_return search_state
      end

      it "calls url_for on the engine scope" do
        expect(my_engine).to receive(:url_for).and_return('/atom-path')
        expect(tag).to match /title="Atom for results"/
        expect(tag).to match /rel="alternate"/
        expect(tag).to match %r{type="application/atom\+xml"}
      end
    end
  end

  describe "should_autofocus_on_search_box?" do
    before do
      allow(Deprecation).to receive(:warn)
    end

    it "is focused if we're on a catalog-like index page without query or facet parameters" do
      allow(helper).to receive_messages(controller: CatalogController.new, action_name: "index", has_search_parameters?: false)
      expect(helper.should_autofocus_on_search_box?).to be true
    end

    it "does not be focused if we're not on a catalog controller" do
      allow(helper).to receive_messages(controller: ApplicationController.new)
      expect(helper.should_autofocus_on_search_box?).to be false
    end

    it "does not be focused if we're not on a catalog controller index" do
      allow(helper).to receive_messages(controller: CatalogController.new, action_name: "show")
      expect(helper.should_autofocus_on_search_box?).to be false
    end

    it "does not be focused if a search parameters are provided" do
      allow(helper).to receive_messages(controller: CatalogController.new, action_name: "index", has_search_parameters?: true)
      expect(helper.should_autofocus_on_search_box?).to be false
    end
  end

  describe "has_thumbnail?" do
    before do
      allow(Deprecation).to receive(:warn)
    end

    let(:document) { SolrDocument.new(data) }
    let(:data) { {} }

    it "has a thumbnail if a thumbnail_method is configured" do
      allow(helper).to receive_messages(blacklight_config: Blacklight::Configuration.new(index: Blacklight::OpenStructWithHashAccess.new(thumbnail_method: :xyz, document_presenter_class: Blacklight::IndexPresenter)))
      expect(helper.has_thumbnail?(document)).to be true
    end

    context "if a thumbnail_field is configured and it exists in the document" do
      let(:data) { { xyz: 'abc' } }

      it "has a thumbnail" do
        allow(helper).to receive_messages(blacklight_config: Blacklight::Configuration.new(index: Blacklight::OpenStructWithHashAccess.new(thumbnail_field: :xyz, document_presenter_class: Blacklight::IndexPresenter)))
        expect(helper.has_thumbnail?(document)).to be true
      end
    end

    it "does not have a thumbnail if the thumbnail_field is missing from the document" do
      allow(helper).to receive_messages(blacklight_config: Blacklight::Configuration.new(index: Blacklight::OpenStructWithHashAccess.new(thumbnail_field: :xyz, document_presenter_class: Blacklight::IndexPresenter)))
      allow(document).to receive_messages(has?: false)
      expect(helper.has_thumbnail?(document)).to be false
    end

    it "does not have a thumbnail if none of the fields are configured" do
      allow(helper).to receive_messages(blacklight_config: Blacklight::Configuration.new(index: Blacklight::OpenStructWithHashAccess.new(document_presenter_class: Blacklight::IndexPresenter)))
      expect(helper).not_to have_thumbnail(document)
    end
  end

  describe "render_thumbnail_tag" do
    let(:index_presenter) do
      instance_double(Blacklight::IndexPresenter, thumbnail: thumbnail_presenter)
    end
    let(:thumbnail_presenter) { instance_double(Blacklight::ThumbnailPresenter) }

    before do
      allow(Deprecation).to receive(:warn)
      allow(helper).to receive(:index_presenter).with(document).and_return(index_presenter)
    end

    let(:document) { instance_double(SolrDocument) }

    it "calls thumbnail presenter with default values" do
      expect(thumbnail_presenter).to receive(:thumbnail_tag).with({}, {})
      helper.render_thumbnail_tag document
    end

    it "calls thumbnail presenter with provided values" do
      expect(thumbnail_presenter).to receive(:thumbnail_tag).with({}, { suppress_link: true })
      helper.render_thumbnail_tag document, {}, suppress_link: true
    end
  end

  describe "thumbnail_url" do
    before do
      allow(Deprecation).to receive(:warn)
    end

    it "pulls the configured thumbnail field out of the document" do
      allow(helper).to receive_messages(blacklight_config: Blacklight::Configuration.new(index: Blacklight::OpenStructWithHashAccess.new(thumbnail_field: :xyz)))
      document = instance_double(SolrDocument)
      allow(document).to receive(:has?).with(:xyz).and_return(true)
      allow(document).to receive(:first).with(:xyz).and_return("asdf")
      expect(helper.thumbnail_url(document)).to eq("asdf")
    end

    it "returns nil if the thumbnail field doesn't exist" do
      allow(helper).to receive_messages(blacklight_config: Blacklight::Configuration.new(index: Blacklight::OpenStructWithHashAccess.new(thumbnail_field: :xyz)))
      document = instance_double(SolrDocument)
      allow(document).to receive(:has?).with(:xyz).and_return(false)
      expect(helper.thumbnail_url(document)).to be_nil
    end
  end

  describe "document_counter_with_offset" do
    it "renders the document index with the appropriate offset" do
      assign(:response, instance_double(Blacklight::Solr::Response, start: 0, grouped?: false))
      expect(helper.document_counter_with_offset(0)).to be(1)
      expect(helper.document_counter_with_offset(1)).to be(2)
    end

    it "renders the document index with the appropriate offset" do
      assign(:response, instance_double(Blacklight::Solr::Response, start: 10, grouped?: false))
      expect(helper.document_counter_with_offset(0)).to be(11)
      expect(helper.document_counter_with_offset(1)).to be(12)
    end

    it "does not provide a counter for grouped responses" do
      assign(:response, instance_double(Blacklight::Solr::Response, start: 10, grouped?: true))
      expect(helper.document_counter_with_offset(0)).to be_nil
    end
  end

  describe "render_document_class" do
    before do
      allow(helper).to receive(:blacklight_config).and_return(blacklight_config)
    end

    let :blacklight_config do
      Blacklight::Configuration.new
    end

    it "pulls data out of a document's field" do
      blacklight_config.index.display_type_field = :type
      doc = { type: 'book' }
      expect(helper.render_document_class(doc)).to eq "blacklight-book"
    end

    it "supports multivalued fields" do
      blacklight_config.index.display_type_field = :type
      doc = { type: %w[book mss] }
      expect(helper.render_document_class(doc)).to eq "blacklight-book blacklight-mss"
    end

    it "supports empty fields" do
      blacklight_config.index.display_type_field = :type
      doc = { type: [] }
      expect(helper.render_document_class(doc)).to be_blank
    end

    it "supports missing fields" do
      blacklight_config.index.display_type_field = :type
      doc = {}
      expect(helper.render_document_class(doc)).to be_blank
    end

    it "supports view-specific field configuration" do
      allow(helper).to receive(:document_index_view_type).and_return(:some_view_type)
      blacklight_config.view.some_view_type(display_type_field: :other_type)
      doc = { other_type: "document" }
      expect(helper.render_document_class(doc)).to eq "blacklight-document"
    end
  end

  describe "#bookmarked?" do
    let(:bookmark) { Bookmark.new document: bookmarked_document }
    let(:bookmarked_document) { SolrDocument.new(id: 'a') }

    before do
      allow(helper).to receive(:current_bookmarks).and_return([bookmark])
    end

    it "is bookmarked if the document is in the bookmarks" do
      expect(helper.bookmarked?(bookmarked_document)).to eq true
    end

    it "does not be bookmarked if the document is not listed in the bookmarks" do
      expect(helper.bookmarked?(SolrDocument.new(id: 'b'))).to eq false
    end
  end

  describe "#render_search_to_page_title_filter" do
    before do
      allow(helper).to receive(:blacklight_config).and_return(blacklight_config)
    end

    let :blacklight_config do
      Blacklight::Configuration.new
    end

    it "renders a facet with a single value" do
      expect(helper.render_search_to_page_title_filter('foo', ['bar'])).to eq "Foo: bar"
    end

    it "renders a facet with two values" do
      expect(helper.render_search_to_page_title_filter('foo', %w[bar baz])).to eq "Foo: bar and baz"
    end

    it "renders a facet with more than two values" do
      expect(helper.render_search_to_page_title_filter('foo', %w[bar baz foobar])).to eq "Foo: 3 selected"
    end
  end

  describe "#render_search_to_page_title" do
    subject { helper.render_search_to_page_title(Blacklight::SearchState.new(params, blacklight_config)) }

    before do
      allow(helper).to receive(:blacklight_config).and_return(blacklight_config)
      allow(helper).to receive(:default_search_field).and_return(Blacklight::Configuration::SearchField.new(key: 'default_search_field', display_label: 'Default'))
      allow(helper).to receive(:label_for_search_field).with(nil).and_return('')
    end

    let(:blacklight_config) do
      Blacklight::Configuration.new.tap do |config|
        config.add_facet_field 'format'
      end
    end

    context 'when the f param is an array' do
      let(:params) { ActionController::Parameters.new(q: 'foobar', f: { format: ["Book"] }) }

      it { is_expected.to eq "foobar / Format: Book" }
    end

    context 'when the f param is not an array' do
      let(:params) { ActionController::Parameters.new(q: 'foobar', f: { format: 'Book' }) }

      it { is_expected.to eq "foobar / Format: Book" }
    end
  end
end