class ForemanSalt::ReportImporter

def self.import(raw, proxy_id = nil)

def self.import(raw, proxy_id = nil)
  raise ::Foreman::Exception, _('Invalid report') unless raw.is_a?(Hash)
  raw.map do |host, report|
    importer = ForemanSalt::ReportImporter.new(host, report, proxy_id)
    importer.import
    report = importer.report
    report.origin = 'Salt'
    report.save!
    report
  end
end

def calculate_metrics

def calculate_metrics
  success = 0
  failed = 0
  changed = 0
  restarted = 0
  restarted_failed = 0
  pending = 0
  time = {}
  @raw.each do |resource, result|
    next unless result.is_a? Hash
    if result['result']
      success += 1
      if resource.match(/^service_/) && result['comment'].include?('restarted')
        restarted += 1
      elsif result['changes'].present?
        changed += 1
      elsif result['pchanges'].present?
        pending += 1
      end
    elsif result['result'].nil?
      pending += 1
    elsif !result['result']
      if resource.match(/^service_/) && result['comment'].include?('restarted')
        restarted_failed += 1
      else
        failed += 1
      end
    end
    duration = if result['duration'].is_a? String
                 begin
                   Float(result['duration'].delete(' ms'))
                 rescue StandardError
                   nil
                 end
               else
                 result['duration']
               end
    # Convert duration from milliseconds to seconds
    duration /= 1000 if duration.is_a? Float
    time[resource] = duration || 0
  end
  time[:total] = time.values.compact.sum || 0
  events = { total: changed + failed + restarted + restarted_failed, success: success + restarted, failure: failed + restarted_failed }
  changes = { total: changed + restarted }
  resources = { 'total' => @raw.size, 'applied' => changed, 'restarted' => restarted, 'failed' => failed,
                'failed_restarts' => restarted_failed, 'skipped' => 0, 'scheduled' => 0, 'pending' => pending }
  { events: events, resources: resources, changes: changes, time: time }
end

def find_or_create_host(host)

def find_or_create_host(host)
  @host ||= Host::Managed.find_by(name: host)
  unless @host
    new = Host::Managed.new(name: host)
    new.save(validate: false)
    @host = new
  end
  @host
end

def import

def import
  logger.info "processing report for #{@host}"
  logger.debug { "Report: #{@raw.inspect}" }
  if @host.new_record? && !Setting[:create_new_host_when_report_is_uploaded]
    logger.info("skipping report for #{@host} as its an unknown host and create_new_host_when_report_is_uploaded setting is disabled")
    return ConfigReport.new
  end
  @host.salt_proxy_id ||= @proxy_id
  @host.last_report = start_time
  if [Array, String].member? @raw.class
    process_failures # If Salt sends us only an array (or string), it's a list of fatal failures
  else
    process_normal
  end
  @host.save(validate: false)
  @host.reload
  @host.refresh_statuses([HostStatus.find_status_by_humanized_name('configuration')])
  logger.info("Imported report for #{@host} in #{(Time.zone.now - start_time).round(2)} seconds")
end

def import_log_messages

def import_log_messages
  @raw.each do |resource, result|
    level = if result['changes'].blank? && result['result']
              :info
            elsif result['result'] == false
              :err
            else
              # nil mean "unchanged" when running highstate with test=True
              :notice
            end
    source = Source.find_or_create_by(value: resource)
    message = if result['changes']['diff']
                result['changes']['diff']
              elsif result['pchanges'].present? && result['pchanges'].include?('diff')
                result['pchanges']['diff']
              elsif result['comment'].presence
                result['comment']
              else
                'No message available'
              end
    message = Message.find_or_create_by(value: message)
    Log.create(message_id: message.id, source_id: source.id, report: @report, level: level)
  end
end

def initialize(host, raw, proxy_id = nil)

def initialize(host, raw, proxy_id = nil)
  @host = find_or_create_host(host)
  @raw = raw
  @proxy_id = proxy_id
end

def process_failures

def process_failures
  @raw = [@raw] unless @raw.is_a? Array
  status = ConfigReportStatusCalculator.new(counters: { 'failed' => @raw.size }).calculate
  @report = ConfigReport.create(host: @host, reported_at: Time.zone.now, status: status, metrics: {})
  source = Source.find_or_create_by(value: 'Salt')
  @raw.each do |failure|
    message = Message.find_or_create_by(value: failure)
    Log.create(message_id: message.id, source_id: source.id, report: @report, level: :err)
  end
end

def process_normal

def process_normal
  metrics = calculate_metrics
  status = ConfigReportStatusCalculator.new(counters: metrics[:resources].slice(*::ConfigReport::METRIC)).calculate
  @report = ConfigReport.new(host: @host, reported_at: start_time, status: status, metrics: metrics)
  return @report unless @report.save
  import_log_messages
end

def start_time

def start_time
  @start_time ||= Time.zone.now
end