# dnfmodule - A puppet package provider for DNF modules
#
# Installing a module:
#  package { 'postgresql':
#   provider => 'dnfmodule',
#   ensure   => '9.6',  # install a specific stream
#   flavor   => 'client',  # install a specific profile
# }


require 'puppet/provider/package'

Puppet::Type.type(:package).provide :dnfmodule, :parent => :dnf do

  has_feature :installable, :uninstallable, :versionable
  #has_feature :upgradeable
  # it's not (yet) feasible to make this upgradeable since module streams don't
  # always have matching version types (i.e. idm has streams DL1 and client,
  # other modules have semver streams, others have string streams... we cannot
  # programatically determine a latest version for ensure => 'latest'

  commands :dnf => '/usr/bin/dnf'

  def self.current_version
    @current_version ||= dnf('--version').split.first
  end

  def self.prefetch(packages)
    if Puppet::Util::Package.versioncmp(current_version, '3.0.1') < 0
      raise Puppet::Error, _("Modules are not supported on DNF versions lower than 3.0.1")
    end
    super
  end

  def self.instances
    packages = []
    cmd = "#{command(:dnf)} module list --installed -d 0 -e #{error_level}"
    execute(cmd).each_line do |line|
      next unless line =~ /\[i\][, ]/  # get rid of non-package lines (including last Hint line)
      line.gsub!(/\[[de]\]/, '')  # we don't care about default/enabled flags
      packages << new(
        name: line.split[0],
        ensure: line.split[1],
        flavor: line.split('[i]').first.split.last,  # this is nasty
        provider: name
      )
    end
    packages
  end

  def query
    pkg = self.class.instances.find do |package|
            @resource[:name] == package.name
          end
    pkg ? pkg.properties : nil
  end

  def reset
    execute([command(:dnf), 'module', 'reset', '-d', '0', '-e', self.class.error_level, '-y', @resource[:name]])
  end

  # to install specific streams and profiles:
  # $ dnf module install module-name:stream/profile
  # $ dnf module install perl:5.24/minimal
  # if unspecified, they will be defaulted (see [d] param in dnf module list output)
  def install
    args = @resource[:name]
    # ensure we start fresh (remove existing stream)
    uninstall unless [:absent, :purged].include?(@property_hash[:ensure])
    case @resource[:ensure]
    when true, false, Symbol
      # pass
    else
      args << ":#{@resource[:ensure]}"
    end
    if @resource[:flavor]
      args << "/#{@resource[:flavor]}"
    end
    execute([command(:dnf), 'module', 'install', '-d', '0', '-e', self.class.error_level, '-y', args])
  end

  def uninstall
    execute([command(:dnf), 'module', 'remove', '-d', '0', '-e', self.class.error_level, '-y', @resource[:name]])
    reset  # reset module to the default stream
  end
end