# Licensed to Elasticsearch B.V. under one or more contributor
# license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright
# ownership. Elasticsearch B.V. licenses this file to you under
# the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.

require 'spec_helper'

describe 'Elasticsearch::Model::Adapter::ActiveRecord Importing' do
  before(:all) do
    ActiveRecord::Schema.define(version: 1) do
      create_table :import_articles do |t|
        t.string   :title
        t.integer  :views
        t.string   :numeric # For the sake of invalid data sent to Elasticsearch
        t.datetime :created_at, default: 'NOW()'
      end
    end

    ImportArticle.delete_all
    ImportArticle.__elasticsearch__.client.cluster.health(wait_for_status: 'yellow')
  end

  before do
    ImportArticle.__elasticsearch__.create_index!
  end

  after do
    clear_indices(ImportArticle)
    clear_tables(ImportArticle)
  end

  describe '#import' do
    context 'when no search criteria is specified' do
      before do
        10.times { |i| ImportArticle.create! title: 'Test', views: i.to_s }
        ImportArticle.import
        ImportArticle.__elasticsearch__.refresh_index!
      end

      it 'imports all documents' do
        expect(ImportArticle.search('*').results.total).to eq(10)
      end

      it "does not pollute the model's namespace" do
        expect(ImportArticle.methods).not_to include(:__transform)
      end
    end

    context 'when batch size is specified' do
      before do
        10.times { |i| ImportArticle.create! title: 'Test', views: "#{i}" }
      end

      let!(:batch_count) do
        batches = 0
        errors  = ImportArticle.import(batch_size: 5) do |response|
          batches += 1
        end
        ImportArticle.__elasticsearch__.refresh_index!
        batches
      end

      it 'imports using the batch size' do
        expect(batch_count).to eq(2)
      end

      it 'imports all the documents' do
        expect(ImportArticle.search('*').results.total).to eq(10)
      end
    end

    context 'when a scope is specified' do
      before do
        10.times { |i| ImportArticle.create! title: 'Test', views: "#{i}" }
        ImportArticle.import(scope: 'popular', force: true)
        ImportArticle.__elasticsearch__.refresh_index!
      end

      it 'applies the scope' do
        expect(ImportArticle.search('*').results.total).to eq(5)
      end
    end

    context 'when a query is specified' do
      before do
        10.times { |i| ImportArticle.create! title: 'Test', views: "#{i}" }
        ImportArticle.import(query: -> { where('views >= 3') })
        ImportArticle.__elasticsearch__.refresh_index!
      end

      it 'applies the query' do
        expect(ImportArticle.search('*').results.total).to eq(7)
      end
    end

    context 'when there are invalid documents' do
      let!(:result) do
        10.times { |i| ImportArticle.create! title: 'Test', views: "#{i}" }
        new_article
        batches = 0
        errors  = ImportArticle.__elasticsearch__.import(batch_size: 5) do |response|
          batches += 1
        end
        ImportArticle.__elasticsearch__.refresh_index!
        { batch_size: batches, errors: errors}
      end

      let(:new_article) do
        ImportArticle.create!(title: "Test INVALID", numeric: "INVALID")
      end

      it 'does not import them' do
        expect(ImportArticle.search('*').results.total).to eq(10)
        expect(result[:batch_size]).to eq(3)
        expect(result[:errors]).to eq(1)
      end
    end

    context 'when a transform proc is specified' do
      before do
        10.times { |i| ImportArticle.create! title: 'Test', views: "#{i}" }
        ImportArticle.import( transform: ->(a) {{ index: { data: { name: a.title, foo: 'BAR' } }}} )
        ImportArticle.__elasticsearch__.refresh_index!
      end

      it 'transforms the documents' do
        expect(ImportArticle.search('*').results.first._source.keys).to include('name')
        expect(ImportArticle.search('*').results.first._source.keys).to include('foo')
      end

      it 'imports all documents' do
        expect(ImportArticle.search('test').results.total).to eq(10)
        expect(ImportArticle.search('bar').results.total).to eq(10)
      end
    end

    context 'when the model has a default scope' do
      around(:all) do |example|
        10.times { |i| ImportArticle.create! title: 'Test', views: "#{i}" }
        ImportArticle.instance_eval { default_scope { where('views > 3') } }
        example.run
        ImportArticle.default_scopes.pop
      end

      before do
        ImportArticle.__elasticsearch__.import
        ImportArticle.__elasticsearch__.refresh_index!
      end

      it 'uses the default scope' do
        expect(ImportArticle.search('*').results.total).to eq(6)
      end
    end

    context 'when there is a default scope and a query specified' do
      around(:all) do |example|
        10.times { |i| ImportArticle.create! title: 'Test', views: "#{i}" }
        ImportArticle.instance_eval { default_scope { where('views > 3') } }
        example.run
        ImportArticle.default_scopes.pop
      end

      before do
        ImportArticle.import(query: -> { where('views <= 4') })
        ImportArticle.__elasticsearch__.refresh_index!
      end

      it 'combines the query and the default scope' do
        expect(ImportArticle.search('*').results.total).to eq(1)
      end
    end

    context 'when the batch is empty' do
      before do
        ImportArticle.delete_all
        ImportArticle.import
        ImportArticle.__elasticsearch__.refresh_index!
      end

      it 'does not make any requests to create documents' do
        expect(ImportArticle.search('*').results.total).to eq(0)
      end
    end
  end
end
