$LOAD_PATH << './lib'
require 'rubygems'

namespace :searchindex do
  task :drop, [:opts] => :environment do |_t, _args|

    # drop indexes
    print 'drop indexes...'
    SearchIndexBackend.index(
      action: 'delete',
    )
    puts 'done'

    Rake::Task['searchindex:drop_pipeline'].execute
  end

  task :create, [:opts] => :environment do |_t, _args|
    print 'create indexes...'

    # es with mapper-attachments plugin
    info = SearchIndexBackend.info
    number = nil
    if info.present?
      number = info['version']['number'].to_s
    end

    settings = {
      'index.mapping.total_fields.limit': 2000,
    }
    mapping = {}
    Models.indexable.each do |local_object|
      mapping.merge!(get_mapping_properties_object(local_object))
    end

    # create indexes
    SearchIndexBackend.index(
      action: 'create',
      data:   {
        mappings: mapping,
        settings: settings,
      }
    )

    if number.blank? || number =~ /^[2-4]\./ || number =~ /^5\.[0-5]\./
      Setting.set('es_pipeline', '')
    end

    puts 'done'

    Rake::Task['searchindex:create_pipeline'].execute
  end

  task :create_pipeline, [:opts] => :environment do |_t, _args|

    # es with mapper-attachments plugin
    info = SearchIndexBackend.info
    number = nil
    if info.present?
      number = info['version']['number'].to_s
    end
    next if number.blank? || number =~ /^[2-4]\./ || number =~ /^5\.[0-5]\./

    # update processors
    pipeline = Setting.get('es_pipeline')
    if pipeline.blank?
      pipeline = "zammad#{rand(999_999_999_999)}"
      Setting.set('es_pipeline', pipeline)
    end
    print 'create pipeline (pipeline)... '
    SearchIndexBackend.processors(
      "_ingest/pipeline/#{pipeline}": [
        {
          action: 'delete',
        },
        {
          action:      'create',
          description: 'Extract zammad-attachment information from arrays',
          processors:  [
            {
              foreach: {
                field:          'article',
                ignore_failure: true,
                processor:      {
                  foreach: {
                    field:          '_ingest._value.attachment',
                    ignore_failure: true,
                    processor:      {
                      attachment: {
                        target_field:   '_ingest._value',
                        field:          '_ingest._value._content',
                        ignore_failure: true,
                      }
                    }
                  }
                }
              }
            }
          ]
        }
      ]
    )
    puts 'done'
  end

  task :drop_pipeline, [:opts] => :environment do |_t, _args|

    # es with mapper-attachments plugin
    info = SearchIndexBackend.info
    number = nil
    if info.present?
      number = info['version']['number'].to_s
    end
    next if number.blank? || number =~ /^[2-4]\./ || number =~ /^5\.[0-5]\./

    # update processors
    pipeline = Setting.get('es_pipeline')
    next if pipeline.blank?

    print 'delete pipeline (pipeline)... '
    SearchIndexBackend.processors(
      "_ingest/pipeline/#{pipeline}": [
        {
          action: 'delete',
        },
      ]
    )
    puts 'done'
  end

  task :reload, [:opts] => :environment do |_t, _args|

    puts 'reload data...'
    Models.indexable.each do |model_class|
      puts " reload #{model_class}"
      started_at = Time.zone.now
      puts "  - started at #{started_at}"
      model_class.search_index_reload
      took = Time.zone.now - started_at
      puts "  - took #{took.to_i} seconds"
    end

  end

  task :rebuild, [:opts] => :environment do |_t, _args|

    Rake::Task['searchindex:drop'].execute
    Rake::Task['searchindex:create'].execute
    Rake::Task['searchindex:reload'].execute

  end
end

=begin

This function will return a index mapping based on the
attributes of the database table of the existing object.

mapping = get_mapping_properties_object(Ticket)

Returns:

mapping = {
  User: {
    properties: {
      firstname: {
        type: 'keyword',
      },
    }
  }
}

=end

def get_mapping_properties_object(object)
  result = {
    object.name => {
      properties: {}
    }
  }

  store_columns = %w[preferences data]

  object.columns_hash.each do |key, value|
    if value.type == :string && value.limit && value.limit <= 5000 && store_columns.exclude?(key)
      result[object.name][:properties][key] = {
        type:   'string',
        fields: {
          raw: { 'type': 'string', 'index': 'not_analyzed' }
        }
      }
    elsif value.type == :integer
      result[object.name][:properties][key] = {
        type: 'integer',
      }
    elsif value.type == :datetime
      result[object.name][:properties][key] = {
        type: 'date',
      }
    elsif value.type == :boolean
      result[object.name][:properties][key] = {
        type:   'boolean',
        fields: {
          raw: { 'type': 'boolean', 'index': 'not_analyzed' }
        }
      }
    elsif value.type == :binary
      result[object.name][:properties][key] = {
        type: 'binary',
      }
    elsif value.type == :bigint
      result[object.name][:properties][key] = {
        type: 'long',
      }
    elsif value.type == :decimal
      result[object.name][:properties][key] = {
        type: 'float',
      }
    elsif value.type == :date
      result[object.name][:properties][key] = {
        type: 'date',
      }
    end
  end

  # es with mapper-attachments plugin
  info = SearchIndexBackend.info
  number = nil
  if info.present?
    number = info['version']['number'].to_s
  end

  if object.name == 'Ticket'

    result[object.name][:_source] = {
      excludes: ['article.attachment']
    }

    if number.blank? || number =~ /^[2-4]\./ || number =~ /^5\.[0-5]\./
      result[object.name][:_source] = {
        excludes: ['article.attachment']
      }
      result[object.name][:properties][:article] = {
        type:              'nested',
        include_in_parent: true,
        properties:        {
          attachment: {
            type: 'attachment',
          }
        }
      }
    end
  end

  result
end
