# TRANSLATORS: do not translate
desc <<-EOT
When Foreman imports a new host by reading its facts it will create the host and
set its model to a value based upon the facts. This can result in a multiplicity
of similarly named models. These may be condensed donw into a few basic models
by comparing the names against a list of model types and regular expressions that
can be found in the config/model.mappings file.
EOT
namespace :models  do
  desc 'Reduce the many vendor supplied model names to a few sensible model definitions'
  task :consolidate, [:dryrun] => :environment do |t, args|
    dryrun = args.dryrun
    # Give ourselves permission to edit stuff
    User.current = User.anonymous_admin

    map_file = "config/model.mappings"
    if File.exist? map_file
      mappings = YAML.load_file map_file
      # Turn off any remote operations that may be called if we modify a host
      if SETTINGS[:unattended]
        puts "Please turn off unattended mode in config/settings.yaml before running this rake task."
        exit(-1)
      end
      names = mappings.map{|m| m["name"]}
      if names.count != names.uniq.count
        puts "There are duplicate entries in the the mapping file: " + (names - names.uniq).to_sentence
        exit(-1)
      end
      consolidate mappings, dryrun
    else
      puts "Unable to find #{map_file}. Please copy this from config/model.mappings.sample or download a newer version from theforeman.org."
    end
  end
end

def consolidate mappings, dryrun
  mapped = []
  for mapping in  mappings
    unless (rex = mapping.delete("rex"))
      puts "No regular expression found for #{mapping["name"]}"
      next
    end
    unless mapping.has_key?("name") and mapping.has_key?("vendor_class") and mapping.has_key?("info") and mapping.has_key?("hardware_model")
      puts "There is a problem with the entry with regular expression #{rex}"
      next
    end
    original_models = Model.count
    matcher = %r{#{rex}}
    if (model = Model.find_by_name(mapping["name"]))
      puts "Using existing model for #{mapping["name"]}"
      model.update_attributes! mapping unless dryrun
    elsif (model = Model.new(mapping))
      puts "Creating new model #{mapping["name"]}"
    end
    for original in Model.all
      if original.name =~ matcher
        puts "Mapping #{original.name} to #{mapping["name"]}"
        mapped << original
        # Validate before we do block assignments
        valid_hosts = []
        for host in original.hosts
          if host.valid?
            valid_hosts << host
          else
            puts "#{host.name}: #{host.errors.full_messages.to_sentence}"
          end
        end
        unless dryrun
          model.hosts << valid_hosts
          model.save(:validate => false)
          if model.errors.empty?
            original.delete if original.hosts.count == 0
          else
            puts "#{model.name} has these errors:" + model.errors.full_messages.to_sentence
          end
        end
      end
    end
  end
  puts "Models that were not recognized and converted: " + (Model.all - mapped).map(&:name).to_sentence
  final_models = Model.count
  puts "Condensed #{original_models} models down into #{final_models}" unless original_models == final_models
end
