# This doesn't get stored in trac, since it changes every time.
providers = Puppet::Util::Reference.newreference :providers, :title => "Provider Suitability Report", :depth => 1, :dynamic => true, :doc => "Which providers are valid for this machine" do
  types = []
  Puppet::Type.loadall
  Puppet::Type.eachtype do |klass|
    next unless klass.providers.length > 0
    types << klass
  end
  types.sort! { |a,b| a.name.to_s <=> b.name.to_s }

  command_line = Puppet::Util::CommandLine.new
  types.reject! { |type| ! command_line.args.include?(type.name.to_s) } unless command_line.args.empty?

  ret = "Details about this host:\n\n"

  # Throw some facts in there, so we know where the report is from.
  ["Ruby Version", "Puppet Version", "Operating System", "Operating System Release"].each do |label|
    name = label.gsub(/\s+/, '')
    value = Facter.value(name)
    ret << option(label, value)
  end
  ret << "\n"

  count = 1

  # Produce output for each type.
  types.each do |type|
    features = type.features
    ret << "\n" # add a trailing newline

    # Now build up a table of provider suitability.
    headers = %w{Provider Suitable?} + features.collect { |f| f.to_s }.sort

    table_data = {}

    functional = false
    notes = []
    default = type.defaultprovider ? type.defaultprovider.name : 'none'
    type.providers.sort { |a,b| a.to_s <=> b.to_s }.each do |pname|
      data = []
      table_data[pname] = data
      provider = type.provider(pname)

      # Add the suitability note
      if missing = provider.suitable?(false) and missing.empty?
        data << "*X*"
        suit = true
        functional = true
      else
        data << "[#{count}]_" # A pointer to the appropriate footnote
        suit = false
      end

      # Add a footnote with the details about why this provider is unsuitable, if that's the case
      unless suit
        details = ".. [#{count}]\n"
        missing.each do |test, values|
          case test
          when :exists
            details << "  - Missing files #{values.join(", ")}\n"
          when :variable
            values.each do |name, facts|
              if Puppet.settings.valid?(name)
                details << "  - Setting #{name} (currently #{Puppet.settings.value(name).inspect}) not in list #{facts.join(", ")}\n"
              else
                details << "  - Fact #{name} (currently #{Facter.value(name).inspect}) not in list #{facts.join(", ")}\n"
              end
            end
          when :true
            details << "  - Got #{values} true tests that should have been false\n"
          when :false
            details << "  - Got #{values} false tests that should have been true\n"
          when :feature
            details << "  - Missing features #{values.collect { |f| f.to_s }.join(",")}\n"
          end
        end
        notes << details

        count += 1
      end

      # Add a note for every feature
      features.each do |feature|
        if provider.features.include?(feature)
          data << "*X*"
        else
          data << ""
        end
      end
    end

    ret << markdown_header(type.name.to_s + "_", 2)

    ret << "[#{type.name}](#{"http://docs.puppetlabs.com/references/stable/type.html##{type.name}"})\n\n"
    ret << option("Default provider", default)
    ret << doctable(headers, table_data)

    notes.each do |note|
      ret << note + "\n"
    end

    ret << "\n"
  end

  ret << "\n"

  ret
end
providers.header = "
Puppet resource types are usually backed by multiple implementations called `providers`,
which handle variance between platforms and tools.

Different providers are suitable or unsuitable on different platforms based on things
like the presence of a given tool.

Here are all of the provider-backed types and their different providers.  Any unmentioned
types do not use providers yet.

"
