# frozen_string_literal: true

RSpec.describe Blacklight::Solr::Response, api: true do
  let(:raw_response) { eval(mock_query_response) }

  let(:config) { Blacklight::Configuration.new }

  let(:r) do
    described_class.new(raw_response,
                        raw_response['params'],
                        blacklight_config: config)
  end

  it 'creates a valid response' do
    expect(r).to respond_to(:header)
  end

  it 'has accurate pagination numbers' do
    expect(r.rows).to eq 11
    expect(r.total).to eq 26
    expect(r.start).to eq 0
  end

  it 'creates a valid response class' do
    expect(r).to respond_to(:response)
    expect(r.docs).to have(11).docs
    expect(r.params[:echoParams]).to eq 'EXPLICIT'

    expect(r).to be_a(Blacklight::Solr::Response::Facets)
  end

  it 'provides facet helpers' do
    expect(r.aggregations.size).to eq 2

    field_names = r.aggregations.collect { |_key, facet| facet.name }
    expect(field_names.include?('cat')).to be true
    expect(field_names.include?('manu')).to be true

    first_facet = r.aggregations['cat']
    expect(first_facet.name).to eq 'cat'

    expect(first_facet.items.size).to eq 10

    expected = "electronics - 14, memory - 3, card - 2, connector - 2, drive - 2, graphics - 2, hard - 2, monitor - 2, search - 2, software - 2"
    received = first_facet.items.collect do |item|
      "#{item.value} - #{item.hits}"
    end.join(', ')

    expect(received).to eq expected

    r.aggregations.each do |_key, facet|
      expect(facet).to respond_to :name
      expect(facet).to respond_to :sort
      expect(facet).to respond_to :offset
      expect(facet).to respond_to :limit
      facet.items.each do |item|
        expect(item).to respond_to :value
        expect(item).to respond_to :hits
      end
    end
  end

  it 'provides a null-object for facets that are not in the response' do
    expect(r.aggregations).not_to include :some_null_object

    expect(r.aggregations[:some_null_object]).to be_a Blacklight::Solr::Response::FacetField
    expect(r.aggregations[:some_null_object].items).to be_blank
  end

  it "provides kaminari pagination helpers" do
    expect(r.limit_value).to eq(r.rows)
    expect(r.offset_value).to eq(r.start)
    expect(r.total_count).to eq(r.total)
    expect(r.next_page).to eq(r.current_page + 1)
    expect(r.prev_page).to be_nil
    expect(r.entry_name(count: 1)).to eq 'entry'
    expect(r.entry_name(count: 2)).to eq 'entries'
    expect(r.size).to eq 26
    if Kaminari.config.respond_to? :max_pages
      expect(r.max_pages).to be_nil
    end
    expect(r).to be_a_kind_of Kaminari::PageScopeMethods
  end

  describe "FacetItem" do
    it "works with a field,value tuple" do
      item = Blacklight::Solr::Response::Facets::FacetItem.new('value', 15)
      expect(item.value).to eq 'value'
      expect(item.hits).to eq 15
    end

    it "works with a field,value + hash triple" do
      item = Blacklight::Solr::Response::Facets::FacetItem.new('value', 15, a: 1, value: 'ignored')
      expect(item.value).to eq 'value'
      expect(item.hits).to eq 15
      expect(item.a).to eq 1
    end

    it "works like an openstruct" do
      item = Blacklight::Solr::Response::Facets::FacetItem.new(value: 'value', hits: 15)

      expect(item.hits).to eq 15
      expect(item.value).to eq 'value'
      expect(item).to be_a_kind_of(OpenStruct)
    end

    it "provides a label accessor" do
      item = Blacklight::Solr::Response::Facets::FacetItem.new('value', hits: 15)
      expect(item.label).to eq 'value'
    end

    it "uses a provided label" do
      item = Blacklight::Solr::Response::Facets::FacetItem.new('value', 15, label: 'custom label')
      expect(item.label).to eq 'custom label'
    end
  end

  it 'returns the correct value when calling facet_by_field_name' do
    facet = r.aggregations['cat']
    expect(facet.name).to eq 'cat'
  end

  it 'provides the responseHeader params' do
    raw_response = eval(mock_query_response)
    raw_response['responseHeader']['params']['test'] = :test
    r = described_class.new(raw_response, raw_response['params'])
    expect(r.params['test']).to eq :test
  end

  it 'extracts json params' do
    raw_response = eval(mock_query_response)
    raw_response['responseHeader']['params']['test'] = 'from query'
    raw_response['responseHeader']['params'].delete('rows')
    raw_response['responseHeader']['params']['json'] = { limit: 5, params: { test: 'from json params' } }.to_json
    r = described_class.new(raw_response, raw_response['params'])
    expect(r.params['test']).to eq 'from query'
    expect(r.rows).to eq 5
  end

  it 'provides the solr-returned params and "rows" should be 11' do
    raw_response = eval(mock_query_response)
    r = described_class.new(raw_response, {})
    expect(r.params[:rows].to_s).to eq '11'
    expect(r.params[:sort]).to eq 'title_si asc, pub_date_si desc'
  end

  it 'provides the ruby request params if responseHeader["params"] does not exist' do
    raw_response = eval(mock_query_response)
    raw_response.delete 'responseHeader'
    r = described_class.new(raw_response, rows: 999, sort: 'score desc, pub_date_si desc, title_si asc')
    expect(r.params[:rows].to_s).to eq '999'
    expect(r.params[:sort]).to eq 'score desc, pub_date_si desc, title_si asc'
  end

  it 'provides spelling suggestions for regular spellcheck results' do
    raw_response = eval(mock_response_with_spellcheck)
    r = described_class.new(raw_response, {})
    expect(r.spelling.words).to include("dell")
    expect(r.spelling.words).to include("ultrasharp")
  end

  it 'provides spelling suggestions for extended spellcheck results' do
    raw_response = eval(mock_response_with_spellcheck_extended)
    r = described_class.new(raw_response, {})
    expect(r.spelling.words).to include("dell")
    expect(r.spelling.words).to include("ultrasharp")
  end

  it 'provides no spelling suggestions when extended results and suggestion frequency is the same as original query frequency' do
    raw_response = eval(mock_response_with_spellcheck_same_frequency)
    r = described_class.new(raw_response, {})
    expect(r.spelling.words).to eq []
  end

  context "pre solr 5 spellcheck collation syntax" do
    it 'provides spelling suggestions for a regular spellcheck results with a collation' do
      raw_response = eval(mock_response_with_spellcheck_collation)
      r = described_class.new(raw_response, {})
      expect(r.spelling.words).to include("dell")
      expect(r.spelling.words).to include("ultrasharp")
    end

    it 'provides spelling suggestion collation' do
      raw_response = eval(mock_response_with_spellcheck_collation)
      r = described_class.new(raw_response, {})
      expect(r.spelling.collation).to eq 'dell ultrasharp'
    end
  end

  context "solr 5 spellcheck collation syntax" do
    it 'provides spelling suggestions for a regular spellcheck results with a collation' do
      raw_response = eval(mock_response_with_spellcheck_collation_solr5)
      r = described_class.new(raw_response, {})
      expect(r.spelling.words).to include("dell")
      expect(r.spelling.words).to include("ultrasharp")
    end

    it 'provides spelling suggestion collation' do
      raw_response = eval(mock_response_with_spellcheck_collation_solr5)
      r = described_class.new(raw_response, {})
      expect(r.spelling.collation).to eq 'dell ultrasharp'
    end
  end

  context 'solr 6.5 spellcheck collation syntax' do
    it 'provides spelling suggestions for a regular spellcheck results with a collation' do
      raw_response = eval(mock_response_with_spellcheck_collation_solr65)
      r = described_class.new(raw_response, {})
      expect(r.spelling.words).to include("dell")
      expect(r.spelling.words).to include("ultrasharp")
    end
  end

  it "provides MoreLikeThis suggestions" do
    raw_response = eval(mock_response_with_more_like_this)
    r = described_class.new(raw_response, {})
    expect(r.more_like(double(id: '79930185'))).to have(2).items
  end

  it "is empty when the response has no results" do
    r = described_class.new({}, {})
    allow(r).to receive_messages(total: 0)
    expect(r).to be_empty
  end

  describe "#export_formats" do
    it "collects the unique export formats for the current response" do
      r = described_class.new({}, {})
      allow(r).to receive_messages(documents: [double(export_formats: { a: 1, b: 2 }), double(export_formats: { b: 1, c: 2 })])
      expect(r.export_formats).to include :a, :b
    end
  end

  def mock_query_response
    %({'responseHeader'=>{'status'=>0,'QTime'=>5,'params'=>{'facet.limit'=>'10','wt'=>'ruby','rows'=>'11','facet'=>'true','facet.field'=>['cat','manu'],'echoParams'=>'EXPLICIT','q'=>'*:*','facet.sort'=>'true', 'sort'=>'title_si asc, pub_date_si desc'}},'response'=>{'numFound'=>26,'start'=>0,'docs'=>[{'id'=>'SP2514N','inStock'=>true,'manu'=>'Samsung Electronics Co. Ltd.','name'=>'Samsung SpinPoint P120 SP2514N - hard drive - 250 GB - ATA-133','popularity'=>6,'price'=>92.0,'sku'=>'SP2514N','timestamp'=>'2009-03-20T14:42:49.795Z','cat'=>['electronics','hard drive'],'spell'=>['Samsung SpinPoint P120 SP2514N - hard drive - 250 GB - ATA-133'],'features'=>['7200RPM, 8MB cache, IDE Ultra ATA-133','NoiseGuard, SilentSeek technology, Fluid Dynamic Bearing (FDB) motor']},{'id'=>'6H500F0','inStock'=>true,'manu'=>'Maxtor Corp.','name'=>'Maxtor DiamondMax 11 - hard drive - 500 GB - SATA-300','popularity'=>6,'price'=>350.0,'sku'=>'6H500F0','timestamp'=>'2009-03-20T14:42:49.877Z','cat'=>['electronics','hard drive'],'spell'=>['Maxtor DiamondMax 11 - hard drive - 500 GB - SATA-300'],'features'=>['SATA 3.0Gb/s, NCQ','8.5ms seek','16MB cache']},{'id'=>'F8V7067-APL-KIT','inStock'=>false,'manu'=>'Belkin','name'=>'Belkin Mobile Power Cord for iPod w/ Dock','popularity'=>1,'price'=>19.95,'sku'=>'F8V7067-APL-KIT','timestamp'=>'2009-03-20T14:42:49.937Z','weight'=>4.0,'cat'=>['electronics','connector'],'spell'=>['Belkin Mobile Power Cord for iPod w/ Dock'],'features'=>['car power adapter, white']},{'id'=>'IW-02','inStock'=>false,'manu'=>'Belkin','name'=>'iPod & iPod Mini USB 2.0 Cable','popularity'=>1,'price'=>11.5,'sku'=>'IW-02','timestamp'=>'2009-03-20T14:42:49.944Z','weight'=>2.0,'cat'=>['electronics','connector'],'spell'=>['iPod & iPod Mini USB 2.0 Cable'],'features'=>['car power adapter for iPod, white']},{'id'=>'MA147LL/A','inStock'=>true,'includes'=>'earbud headphones, USB cable','manu'=>'Apple Computer Inc.','name'=>'Apple 60 GB iPod with Video Playback Black','popularity'=>10,'price'=>399.0,'sku'=>'MA147LL/A','timestamp'=>'2009-03-20T14:42:49.962Z','weight'=>5.5,'cat'=>['electronics','music'],'spell'=>['Apple 60 GB iPod with Video Playback Black'],'features'=>['iTunes, Podcasts, Audiobooks','Stores up to 15,000 songs, 25,000 photos, or 150 hours of video','2.5-inch, 320x240 color TFT LCD display with LED backlight','Up to 20 hours of battery life','Plays AAC, MP3, WAV, AIFF, Audible, Apple Lossless, H.264 video','Notes, Calendar, Phone book, Hold button, Date display, Photo wallet, Built-in games, JPEG photo playback, Upgradeable firmware, USB 2.0 compatibility, Playback speed control, Rechargeable capability, Battery level indication']},{'id'=>'TWINX2048-3200PRO','inStock'=>true,'manu'=>'Corsair Microsystems Inc.','name'=>'CORSAIR  XMS 2GB (2 x 1GB) 184-Pin DDR SDRAM Unbuffered DDR 400 (PC 3200) Dual Channel Kit System Memory - Retail','popularity'=>5,'price'=>185.0,'sku'=>'TWINX2048-3200PRO','timestamp'=>'2009-03-20T14:42:49.99Z','cat'=>['electronics','memory'],'spell'=>['CORSAIR  XMS 2GB (2 x 1GB) 184-Pin DDR SDRAM Unbuffered DDR 400 (PC 3200) Dual Channel Kit System Memory - Retail'],'features'=>['CAS latency 2,	2-3-3-6 timing, 2.75v, unbuffered, heat-spreader']},{'id'=>'VS1GB400C3','inStock'=>true,'manu'=>'Corsair Microsystems Inc.','name'=>'CORSAIR ValueSelect 1GB 184-Pin DDR SDRAM Unbuffered DDR 400 (PC 3200) System Memory - Retail','popularity'=>7,'price'=>74.99,'sku'=>'VS1GB400C3','timestamp'=>'2009-03-20T14:42:50Z','cat'=>['electronics','memory'],'spell'=>['CORSAIR ValueSelect 1GB 184-Pin DDR SDRAM Unbuffered DDR 400 (PC 3200) System Memory - Retail']},{'id'=>'VDBDB1A16','inStock'=>true,'manu'=>'A-DATA Technology Inc.','name'=>'A-DATA V-Series 1GB 184-Pin DDR SDRAM Unbuffered DDR 400 (PC 3200) System Memory - OEM','popularity'=>5,'sku'=>'VDBDB1A16','timestamp'=>'2009-03-20T14:42:50.004Z','cat'=>['electronics','memory'],'spell'=>['A-DATA V-Series 1GB 184-Pin DDR SDRAM Unbuffered DDR 400 (PC 3200) System Memory - OEM'],'features'=>['CAS latency 3,	 2.7v']},{'id'=>'3007WFP','inStock'=>true,'includes'=>'USB cable','manu'=>'Dell, Inc.','name'=>'Dell Widescreen UltraSharp 3007WFP','popularity'=>6,'price'=>2199.0,'sku'=>'3007WFP','timestamp'=>'2009-03-20T14:42:50.017Z','weight'=>401.6,'cat'=>['electronics','monitor'],'spell'=>['Dell Widescreen UltraSharp 3007WFP'],'features'=>['30" TFT active matrix LCD, 2560 x 1600, .25mm dot pitch, 700:1 contrast']},{'id'=>'VA902B','inStock'=>true,'manu'=>'ViewSonic Corp.','name'=>'ViewSonic VA902B - flat panel display - TFT - 19"','popularity'=>6,'price'=>279.95,'sku'=>'VA902B','timestamp'=>'2009-03-20T14:42:50.034Z','weight'=>190.4,'cat'=>['electronics','monitor'],'spell'=>['ViewSonic VA902B - flat panel display - TFT - 19"'],'features'=>['19" TFT active matrix LCD, 8ms response time, 1280 x 1024 native resolution']},{'id'=>'0579B002','inStock'=>true,'manu'=>'Canon Inc.','name'=>'Canon PIXMA MP500 All-In-One Photo Printer','popularity'=>6,'price'=>179.99,'sku'=>'0579B002','timestamp'=>'2009-03-20T14:42:50.062Z','weight'=>352.0,'cat'=>['electronics','multifunction printer','printer','scanner','copier'],'spell'=>['Canon PIXMA MP500 All-In-One Photo Printer'],'features'=>['Multifunction ink-jet color photo printer','Flatbed scanner, optical scan resolution of 1,200 x 2,400 dpi','2.5" color LCD preview screen','Duplex Copying','Printing speed up to 29ppm black, 19ppm color','Hi-Speed USB','memory card: CompactFlash, Micro Drive, SmartMedia, Memory Stick, Memory Stick Pro, SD Card, and MultiMediaCard']}]},'facet_counts'=>{'facet_queries'=>{},'facet_fields'=>{'cat'=>['electronics',14,'memory',3,'card',2,'connector',2,'drive',2,'graphics',2,'hard',2,'monitor',2,'search',2,'software',2],'manu'=>['inc',8,'apach',2,'belkin',2,'canon',2,'comput',2,'corp',2,'corsair',2,'foundat',2,'microsystem',2,'softwar',2]},'facet_dates'=>{}}})
  end

  # These spellcheck responses are all Solr 1.4 responses
  def mock_response_with_spellcheck
    %({'responseHeader'=>{'status'=>0,'QTime'=>9,'params'=>{'spellcheck'=>'true','spellcheck.collate'=>'true','wt'=>'ruby','q'=>'hell ultrashar'}},'response'=>{'numFound'=>0,'start'=>0,'docs'=>[]},'spellcheck'=>{'suggestions'=>['hell',{'numFound'=>1,'startOffset'=>0,'endOffset'=>4,'suggestion'=>['dell']},'ultrashar',{'numFound'=>1,'startOffset'=>5,'endOffset'=>14,'suggestion'=>['ultrasharp']},'collation','dell ultrasharp']}})
  end

  def mock_response_with_spellcheck_extended
    %({'responseHeader'=>{'status'=>0,'QTime'=>8,'params'=>{'spellcheck'=>'true','spellcheck.collate'=>'true','wt'=>'ruby','spellcheck.extendedResults'=>'true','q'=>'hell ultrashar'}},'response'=>{'numFound'=>0,'start'=>0,'docs'=>[]},'spellcheck'=>{'suggestions'=>['hell',{'numFound'=>1,'startOffset'=>0,'endOffset'=>4,'origFreq'=>0,'suggestion'=>[{'word'=>'dell','freq'=>1}]},'ultrashar',{'numFound'=>1,'startOffset'=>5,'endOffset'=>14,'origFreq'=>0,'suggestion'=>[{'word'=>'ultrasharp','freq'=>1}]},'correctlySpelled',false,'collation','dell ultrasharp']}})
  end

  def mock_response_with_spellcheck_same_frequency
    %({'responseHeader'=>{'status'=>0,'QTime'=>8,'params'=>{'spellcheck'=>'true','spellcheck.collate'=>'true','wt'=>'ruby','spellcheck.extendedResults'=>'true','q'=>'hell ultrashar'}},'response'=>{'numFound'=>0,'start'=>0,'docs'=>[]},'spellcheck'=>{'suggestions'=>['hell',{'numFound'=>1,'startOffset'=>0,'endOffset'=>4,'origFreq'=>1,'suggestion'=>[{'word'=>'dell','freq'=>1}]},'ultrashard',{'numFound'=>1,'startOffset'=>5,'endOffset'=>14,'origFreq'=>1,'suggestion'=>[{'word'=>'ultrasharp','freq'=>1}]},'correctlySpelled',false,'collation','dell ultrasharp']}})
  end

  # it can be the case that extended results are off and collation is on
  def mock_response_with_spellcheck_collation
    %({'responseHeader'=>{'status'=>0,'QTime'=>3,'params'=>{'spellspellcheck.build'=>'true','spellcheck'=>'true','q'=>'hell','spellcheck.q'=>'hell ultrashar','wt'=>'ruby','spellcheck.collate'=>'true'}},'response'=>{'numFound'=>0,'start'=>0,'docs'=>[]},'spellcheck'=>{'suggestions'=>['hell',{'numFound'=>1,'startOffset'=>0,'endOffset'=>4,'suggestion'=>['dell']},'ultrashar',{'numFound'=>1,'startOffset'=>5,'endOffset'=>14,'suggestion'=>['ultrasharp']},'collation','dell ultrasharp']}})
  end

  def mock_response_with_spellcheck_collation_solr5
    %({'responseHeader'=>{'status'=>0,'QTime'=>3,'params'=>{'spellspellcheck.build'=>'true','spellcheck'=>'true','q'=>'hell','spellcheck.q'=>'hell ultrashar','wt'=>'ruby','spellcheck.collate'=>'true'}},'response'=>{'numFound'=>0,'start'=>0,'docs'=>[]},'spellcheck'=>{'suggestions'=>['hell',{'numFound'=>1,'startOffset'=>0,'endOffset'=>4,'suggestion'=>['dell']},'ultrashar',{'numFound'=>1,'startOffset'=>5,'endOffset'=>14,'suggestion'=>['ultrasharp']}],'collations'=>['collation','dell ultrasharp']}})
  end

  def mock_response_with_spellcheck_collation_solr65
    %({'responseHeader'=>{'status'=>0,'QTime'=>3,'params'=>{'spellspellcheck.build'=>'true','spellcheck'=>'true','q'=>'hell','spellcheck.q'=>'hell ultrashar','wt'=>'ruby','spellcheck.collate'=>'true'}},'response'=>{'numFound'=>0,'start'=>0,'docs'=>[]},'spellcheck'=>{'suggestions'=>{'hell'=>{'numFound'=>1,'startOffset'=>0,'endOffset'=>4,'suggestion'=>['dell']},'ultrashar'=>{'numFound'=>1,'startOffset'=>5,'endOffset'=>14,'suggestion'=>['ultrasharp']}},'collations'=>['collation','dell ultrasharp']}})
  end

  def mock_response_with_more_like_this
    %({'responseHeader'=>{'status'=>0,'QTime'=>8,'params'=>{'facet'=>'false','mlt.mindf'=>'1','mlt.fl'=>'subject_tsim','fl'=>'id','mlt.count'=>'3','mlt.mintf'=>'0','mlt'=>'true','q.alt'=>'*:*','qt'=>'search','wt'=>'ruby'}},'response'=>{'numFound'=>30,'start'=>0,'docs'=>[{'id'=>'00282214'},{'id'=>'00282371'},{'id'=>'00313831'},{'id'=>'00314247'},{'id'=>'43037890'},{'id'=>'53029833'},{'id'=>'77826928'},{'id'=>'78908283'},{'id'=>'79930185'},{'id'=>'85910001'}]},'moreLikeThis'=>{'00282214'=>{'numFound'=>0,'start'=>0,'docs'=>[]},'00282371'=>{'numFound'=>0,'start'=>0,'docs'=>[]},'00313831'=>{'numFound'=>1,'start'=>0,'docs'=>[{'id'=>'96933325'}]},'00314247'=>{'numFound'=>3,'start'=>0,'docs'=>[{'id'=>'2008543486'},{'id'=>'96933325'},{'id'=>'2009373513'}]},'43037890'=>{'numFound'=>0,'start'=>0,'docs'=>[]},'53029833'=>{'numFound'=>0,'start'=>0,'docs'=>[]},'77826928'=>{'numFound'=>1,'start'=>0,'docs'=>[{'id'=>'94120425'}]},'78908283'=>{'numFound'=>0,'start'=>0,'docs'=>[]},'79930185'=>{'numFound'=>2,'start'=>0,'docs'=>[{'id'=>'94120425'},{'id'=>'2007020969'}]},'85910001'=>{'numFound'=>0,'start'=>0,'docs'=>[]}}})
  end
end