module YamlImportExportMixin
  extend ActiveSupport::Concern

  module ClassMethods
    def export_to_array(list, klass)
      begin
        klass = Object.const_get(klass) unless klass.kind_of?(Class)
      rescue => err
        _log.error("List: [#{list}], Class: [#{klass}] - #{err.message}")
        return []
      end

      list = klass.where(:id => list) unless list.first.kind_of?(klass)
      list.collect { |obj| obj.export_to_array if obj }.compact.flatten
    end

    def export_to_yaml(list, klass)
      export_to_array(list, klass).to_yaml
    end

    # Import from a file that the user selects to upload
    #
    # @param fd [Integer] file descriptor of the file to be uploaded.
    # @param options [Hash] The properties to change.
    # @option options [Boolean] :save Save into DB the objects to be imported.
    # @option options [Boolean] :overwrite Overwrite the exsiting object.
    # @option options [String] :userid The current user's id, used to check the
    #     user's accessibility to the objects to be imported.
    # @return [Array<Hash>, Array<String>] The array of objects to be imported,
    #   and the array of importing status.
    def import(fd, options = {})
      fd.rewind   # ensure to be at the beginning as the file is read multiple times
      begin
        reps = YAML.safe_load(fd.read,
          :aliases           => true,
          :permitted_classes => [
            ActiveSupport::TimeWithZone,
            ActiveSupport::TimeZone,
            Date,
            DateTime,
            MiqExpression,
            Symbol,
            Time,
          ]
        )
      rescue => err
        _log.error("Failed to load from #{fd}: #{err}")
        raise "Invalid YAML file"
      end

      validate_import_data_class(reps)

      return reps, import_from_array(reps, options)
    end

    def validate_import_data_class(data)
      data_class_name = data.first.keys.first

      unless self::IMPORT_CLASS_NAMES.include?(data_class_name)
        raise _("Incorrect format: Expected %{expected_class} and received %{received_class}.") % {
          :expected_class => self::IMPORT_CLASS_NAMES.join(", "),
          :received_class => data_class_name
        }
      end
    end

    # Import from an array of hash of the objects
    #
    # @param input [Array] The objects to be imported.
    # @param options [Hash] The properties to change.
    # @option (see #import)
    # @return [Array<String>] The array of importing status.
    def import_from_array(input, options = {})
      input.collect do |i|
        begin
          klass = Object.const_get(i.keys.first)
          report = i[klass.to_s]
        rescue
          # for the legacy MiqReport
          klass = MiqReport
          report = i
        end

        _, stat = klass.import_from_hash(report.deep_clone, options)
        stat
      end
    end
  end
end
