namespace :load do

task :defaults do
  set :chewy_default_hooks, -> { true }
  set :chewy_conditionally_reset, -> { true }
  set :chewy_path, -> { 'app/chewy' }
  set :chewy_env, -> { fetch(:rails_env, fetch(:stage)) }
  set :chewy_role, -> { :app }
  set :chewy_delete_removed_indexes, -> { true }
end

end

namespace :deploy do

before :starting, :check_chewy_hooks do
  invoke :'chewy:add_default_hooks' if fetch(:chewy_default_hooks)
end

end

namespace :chewy do

def delete_indexes(index_files)
  index_classes = index_files.map { |file| File.basename(file, '.rb').camelize }.uniq
  runner_code = "[#{index_classes.join(', ')}].each(&:delete)"

  # Removed index files exists only in the old (current) release
  within current_path do
    with rails_env: fetch(:chewy_env) do
      info "Removing indexes: #{index_classes.join(',')}"
      execute :rails, "runner '#{runner_code}'"
    end
  end
end

def reset_modified_indexes(index_files)
  index_names = index_files.map { |file| File.basename(file, '_index.rb') }.join(',')

  within release_path do
    with rails_env: fetch(:chewy_env) do
      info "Modified or new indexes: #{index_names}"
      execute :rake, "chewy:reset[#{index_names}]"
    end
  end
end

def run_default_chewy_task(task_name, indexes)
  on roles fetch(:chewy_role) do
    within release_path do
      with rails_env: fetch(:chewy_env) do
        if indexes.any?
          execute :rake, "chewy:#{task_name}[#{indexes.join(',')}]"
        else
          # Simply chewy:reset / chewy:update for Chewy > 0.8.4
          execute :rake, "chewy:#{task_name}"
        end
      end
    end
  end
end

# Adds default Capistrano::Chewy hooks to the deploy flow
task :add_default_hooks do
  after :'deploy:updated', 'chewy:rebuild'
  after :'deploy:reverted', 'chewy:rebuild'
end

# Default Chewy rake tasks
desc 'Destroy, recreate and import data to all or specified (pass with [one,two]) indexes'
task :reset do |_task, args|
  run_default_chewy_task(:reset, args.extras)
end

desc 'Updates data to all or specified (passed with [one,two]) indexes'
task :update do |_task, args|
  run_default_chewy_task(:update, args.extras)
end

# Smart rebuild of modified Chewy indexes
desc 'Reset Chewy indexes if they have been added, changed or removed'
task :rebuild do
  on roles fetch(:chewy_role) do
    info "Checking Chewy directory (#{fetch(:chewy_path)})"

    chewy_path = File.join(release_path, fetch(:chewy_path))
    unless test("[ -d #{chewy_path} ]")
      error "Directory #{chewy_path} doesn't exist!"
      exit 1
    end

    if fetch(:chewy_conditionally_reset)
      if test('diff -v')
        info 'Running smart indexes reset...'
        invoke :'chewy:rebuilding'
      else
        error "Can't check the difference between Chewy indexes - install 'diff' tool first!"
        exit 1
      end
    else
      info 'Running chewy:reset'
      invoke :'chewy:reset'
    end
  end
end

desc 'Runs smart Chewy indexes rebuilding (only for changed files)'
task :rebuilding do
  on roles fetch(:chewy_role) do
    chewy_path = fetch(:chewy_path)
    info "Checking changes in #{chewy_path}"

    chewy_release_path = File.join(release_path, chewy_path)
    chewy_current_path = File.join(current_path, chewy_path)

    # -q, --brief                     report only when files differ
    # -E, --ignore-tab-expansion      ignore changes due to tab expansion
    # -Z, --ignore-trailing-space     ignore white space at line end
    # -B, --ignore-blank-lines        ignore changes where lines are all blank
    #
    diff_args = "-qZEB #{chewy_release_path} #{chewy_current_path}"
    indexes_diff = capture :diff, diff_args, raise_on_non_zero_exit: false
    changes = ::CapistranoChewy::DiffParser.parse(indexes_diff, chewy_current_path, chewy_release_path)

    # If diff is empty then indices have not changed
    if changes.empty?
      info 'Skipping `chewy:rebuilding` (nothing changed in the Chewy path)'
    else
      indexes_to_reset = changes.changed.concat(changes.added)
      indexes_to_delete = changes.removed

      # Reset indexes which have been modified or added
      reset_modified_indexes(indexes_to_reset) if indexes_to_reset.any?

      # Delete indexes which have been removed
      if indexes_to_delete.any? && fetch(:chewy_delete_removed_indexes)
        delete_indexes(indexes_to_delete)
      end
    end
  end
end

end