require 'spec_helper'

describe Barcoder::Bucket do

  context "when the server cannot be reached" do
    before(:each) do
      @orig_host = Barcoder::Config.host
      @orig_timeout = Barcoder::Config.timeout_ms
      Barcoder::Config.timeout_ms = 10
      Barcoder::Config.host = 'http://unreachable'
    end

    after(:each) do
      Barcoder::Config.host = @orig_host
      Barcoder::Config.timeout_ms = @orig_timeout
    end

    it "raises an error upon making a request" do
      expect { Barcoder::Bucket.all}.to raise_error(Barcoder::ConnectionError)
    end
  end

  context ".all" do
    it "loads all buckets" do
      buckets = Barcoder::Bucket.all
      buckets.size.should be > 1

      buckets.first.persisted.should be_true
    end

    it "loads buckets by token" do
      first_token = Barcoder::Bucket.all.first.token

      buckets = Barcoder::Bucket.all([first_token])
      buckets.size.should eq 1
      buckets.first.token.should eq first_token
    end
  end

  context "Get an existing bucket" do
    before(:each) do
      @bucket = Barcoder::Bucket.get('123testbucket')
    end

    it "fetches bucket fields" do
      @bucket.should be_a Barcoder::Bucket
      @bucket.token.should eq '123testbucket'
      @bucket.persisted.should be_true
      @bucket.email_token.should be_present
    end

    context "#save" do
      it "updates the existing bucket" do
        token = @bucket.token # this is how we can ensure we get the same bucket
        @bucket.name = 'updated name'

        @bucket.save.should be_true

        @bucket.name.should eq 'updated name'
        @bucket.token.should eq token
        @bucket.persisted.should be_true
        @bucket.email_token.should be_present
      end
    end

    it "#decode_from_file" do
      Barcoder::PDF.should_receive(:decode_from_file).
        with(@bucket, '/test/file', {})
      @bucket.decode_from_file('/test/file')
    end

    it "#decode_from_string" do
      Barcoder::PDF.should_receive(:decode_from_string).
        with(@bucket, 'content', {})
      @bucket.decode_from_string('content')
    end
  end

  context "#search_messages" do
    before(:each) do
      @bucket = test_bucket
      @page = @bucket.partial_imports.first.pages.first
    end

    it "return array of like messages" do
      @bucket.search_messages(@page.message).should eq [@page.message]
    end
  end

  context "a new bucket" do
    before(:each) do
      @bucket = Barcoder::Bucket.new(name: 'new bucket', webhook: 'url')
    end

    it "should not be persisted" do
      @bucket.persisted.should be_false
    end

    it "should be valid with a name" do
      @bucket.valid?.should be_true
      @bucket.name = nil
      @bucket.valid?.should be_false
    end

    it "should not allow decoding without a token" do
      @bucket.decode_from_file('test').should be_false
      @bucket.decode_from_string('test').should be_false
    end

    context "#save" do
      it "returns false when invalid" do
        @bucket.stub(:valid?).and_return(false)
        @bucket.save.should be_false
      end

      it "persists and updates with new data from server when valid" do
        @bucket.save.should be_true
        @bucket = Barcoder::Bucket.get(@bucket.token)

        @bucket.name.should eq 'new bucket'
        @bucket.webhook.should eq 'url'
        @bucket.token.should_not be_nil
        @bucket.persisted.should be_true
      end
    end
  end

  context "#regenerate_email_token" do
    before(:each) do
      @bucket = Barcoder::Bucket.new(name: 'new bucket')
      @bucket.stub(:persisted).and_return(true)
      @bucket.stub(:valid?).and_return(true)
    end

    it "returns false if bucket is new" do
      @bucket.stub(:persisted).and_return(false)
      expect(@bucket.regenerate_email_token).to be_false
    end

    it "returns false if bucket is invalid" do
      @bucket.stub(:valid?).and_return(false)
      expect(@bucket.regenerate_email_token).to be_false
    end

    it "requests a new email_token to be generated" do
      @bucket = Barcoder::Bucket.get('123testbucket')
      orig_email_token = @bucket.email_token

      new_token = @bucket.regenerate_email_token

      expect(new_token).to_not eq orig_email_token
      expect(new_token).to eq @bucket.email_token
    end
  end

  context "#can_merge?" do
    before(:each) do
      @pdf1 = Barcoder::PDF.new(
        total_pages_count: 2, missing_pages_count: 1,
        pages: [Barcoder::Page.new(matched: true), Barcoder::Page.new(matched: false)])
      @pdf2 = Barcoder::PDF.new(
        total_pages_count: 2, missing_pages_count: 1,
        pages: [Barcoder::Page.new(matched: false), Barcoder::Page.new(matched: true)])
      @pdfs = [@pdf1, @pdf2]
    end

    it "should be true when all conditions met" do
      Barcoder::Bucket.new.can_merge?(@pdfs).should be_true
    end

    context "should not be true when" do
      it "has only none or one pdf" do
        Barcoder::Bucket.new.can_merge?([]).should be_false
        Barcoder::Bucket.new.can_merge?([@pdf1]).should be_false
      end

      it "has no version of a given page" do
        @pdf2.pages.first.matched = true
        @pdf2.pages.last.matched = false
        Barcoder::Bucket.new.can_merge?(@pdfs).should be_false
      end

      it "PDFs have different page counts" do
        @pdf2.total_pages_count += 1
        Barcoder::Bucket.new.can_merge?(@pdfs).should be_false
      end
    end
  end

  context "#merge" do
    before(:each) do
      @bucket = test_bucket
      @pdfs = test_bucket.partial_imports
    end

    it "is true when successful" do
      @bucket.merge(@pdfs).should be_true
    end

    it "is false when unsuccessful" do
      # Using non-existent IDs leads to an unsuccessful merge
      @bucket.merge([987, 8971]).should be_false
    end
  end

  context "#recent_imports" do
    before(:each) do
      @bucket = test_bucket
      @bucket.recent_imports(Time.now)
    end

    it "caches recent_imports" do
      @bucket.should_not_receive(:recent_imports_request)
      @bucket.recent_imports(Time.now)
    end

    it "breaks the cache when requested" do
      @bucket.should_receive(:recent_imports).
        and_return(mock(run: true))
      @bucket.recent_imports(Time.now, false)
    end
  end

  context "#active_imports" do
    before(:each) do
      @bucket = test_bucket
      @bucket.active_imports
    end

    it "caches active_imports" do
      @bucket.should_not_receive(:active_imports_request)
      @bucket.active_imports
    end

    it "gets active_imports as Barcoder::Import" do
      first = @bucket.active_imports.first
      first.should be_a Barcoder::Import
      first.page_count.should eq 2
      first.source.should eq 'test@example.com'
    end

    it "breaks the cache when requested" do
      @bucket.should_receive(:active_imports_request).
        and_return(mock(run: true))
      @bucket.active_imports(false)
    end
  end

  context "#queue_" do
    before(:each) do
      @bucket = test_bucket
      @bucket.queue_unmatched_pages
    end

    it "should not run the request yet" do
      @bucket.instance_eval("@unmatched_pages_requests").should be_nil
    end

    it "should fetch the unmatched_pages when queue run" do
      Barcoder::Config.hydra.run
      @bucket.instance_eval("@unmatched_pages_requests").should be_a Hash
    end
  end
end

describe Barcoder::Bucket, '#concatenate_unmatched_pages' do
  it 'requests for the page ids to be concatenated' do
    bucket = Barcoder::Bucket.all.first
    page_ids = [bucket.unmatched_pages.last.id]
    data = { 'key' => 'value' }

    result = bucket.concatenate_unmatched_pages(page_ids, data)

    expect(result).to be_a Barcoder::PDF
    expect(result.data).to eq data
    expect(result).to have(1).page
    expect(result.pages.first.id).to eq page_ids.first
  end
end

describe Barcoder::Bucket, '#merge_buckets' do
  it 'merges other buckets into this bucket' do
    bucket_count = Barcoder::Bucket.all.size
    bucket = Barcoder::Bucket.new(token: 'merge1')

    result = bucket.merge_buckets(['merge2', 'merge3'])
    existing_bucket_tokens = Barcoder::Bucket.all.map(&:token)

    expect(result).to be_true
    expect(existing_bucket_tokens).to have(bucket_count - 2).items
    expect(existing_bucket_tokens).to include 'merge1'
    expect(existing_bucket_tokens).to_not include 'merge2'
  end
end

describe Barcoder::Bucket, '#unmatched_pages' do
  it 'fetches unmatched pages with urls' do
    first_page = test_bucket.unmatched_pages.first

    expect(first_page).to be_a Barcoder::Page
    expect(first_page.url).to match 'https://s3.amazonaws.com'
  end

  context 'without urls' do
    it 'fetches unmatched pages without urls' do
      first_page = test_bucket.unmatched_pages(with_urls: false).first
      expect(first_page).to be_a Barcoder::Page
      expect(first_page.url).to be_nil
    end
  end
end

describe Barcoder::Bucket, '#unmatched_files' do
  it 'fetches unmatched files with page urls' do
    bucket = test_bucket

    result = bucket.unmatched_files
    first_file = result.first

    expect(first_file).to be_a Barcoder::Import
  end
end

describe Barcoder::Bucket, '#partial_imports' do
  it 'fetches partial imports' do
    bucket = test_bucket

    result = bucket.partial_imports
    first_file = result.first
    matched_page = first_file.pages.detect(&:matched?)

    expect(first_file).to be_a Barcoder::PDF
    expect(matched_page.url).to match 'https://s3.amazonaws.com/'
  end

  context 'with_urls false' do
    it 'fetches partial imports without urls' do
      bucket = test_bucket

      result = bucket.partial_imports(with_urls: false)
      first_file = result.first
      matched_page = first_file.pages.detect(&:matched?)

      expect(matched_page.url).to be_nil
    end
  end
end

describe Barcoder::Bucket, '#update_barcoded_files_data_key' do
  it 'updates the barcoded files data key' do
    bucket = test_bucket

    result = bucket.update_barcoded_files_data_key('key', 'old', 'new')

    expect(result).to be_true
  end
end

describe Barcoder::Bucket, "#imports_by_ids" do
  it "caches active_imports" do
    bucket = test_bucket
    bucket.imports_by_ids([1, 2])

    bucket.should_not_receive(:imports_by_ids_request)

    bucket.imports_by_ids([1, 2])
  end

  it "gets active_imports as Barcoder::Import" do
    bucket = test_bucket
    import = bucket.active_imports.first

    result = bucket.imports_by_ids([import.id])

    result.should be_a Array
    result.first.should be_a Barcoder::Import
    result.first.page_count.should eq 2
    result.first.source.should eq 'test@example.com'
  end
end
