# frozen_string_literal: true

Ohai.plugin(:Passwd) do
  provides "etc", "current_user"
  optional true

  # @param [String] str
  #
  # @return [String]
  #
  def fix_encoding(str)
    str.force_encoding(Encoding.default_external) if str.respond_to?(:force_encoding)
    str
  end

  collect_data do
    require "etc" unless defined?(Etc)

    unless etc
      etc Mash.new

      etc[:passwd] = Mash.new
      etc[:group] = Mash.new

      Etc.passwd do |entry|
        user_passwd_entry = Mash.new(dir: entry.dir, gid: entry.gid, uid: entry.uid, shell: entry.shell, gecos: entry.gecos)
        user_passwd_entry.each_value { |v| fix_encoding(v) }
        entry_name = fix_encoding(entry.name)
        etc[:passwd][entry_name] = user_passwd_entry unless etc[:passwd].key?(entry_name)
      end

      Etc.group do |entry|
        group_entry = Mash.new(gid: entry.gid,
          members: entry.mem.map { |u| fix_encoding(u) })

        etc[:group][fix_encoding(entry.name)] = group_entry
      end
    end

    unless current_user
      current_user fix_encoding(Etc.getpwuid(Process.euid).name)
    end
  end

  collect_data(:windows) do
    require "wmi-lite/wmi" unless defined?(WmiLite::Wmi)

    unless etc
      etc Mash.new

      wmi = WmiLite::Wmi.new

      etc[:passwd] = Mash.new
      users = wmi.query("SELECT * FROM Win32_UserAccount WHERE LocalAccount = True")
      users.each do |user|
        uname = user["Name"].strip.downcase
        Ohai::Log.debug("processing user #{uname}")
        etc[:passwd][uname] = Mash.new
        wmi_obj = user.wmi_ole_object
        wmi_obj.properties_.each do |key|
          etc[:passwd][uname][key.name.downcase] = user[key.name]
        end
      end

      etc[:group] = Mash.new
      groups = wmi.query("SELECT * FROM Win32_Group WHERE LocalAccount = True")
      groups.each do |group|
        gname = group["Name"].strip.downcase
        Ohai::Log.debug("processing group #{gname}")
        etc[:group][gname] = Mash.new
        wmi_obj = group.wmi_ole_object
        wmi_obj.properties_.each do |key|
          etc[:group][gname][key.name.downcase] = group[key.name]
        end

        # This is the primary reason that we're using WMI instead of powershell
        # cmdlets - the powershell start up cost is huge, and you *must* do this
        # query for every. single. group. individually.

        # The query returns nothing unless you specify domain *and* name, it's
        # a path, not a set of queries.
        subq = "Win32_Group.Domain='#{group["Domain"]}',Name='#{group["Name"]}'"
        members = wmi.query(
          "SELECT * FROM Win32_GroupUser WHERE GroupComponent=\"#{subq}\""
        )
        etc[:group][gname]["members"] = members.map do |member|
          mi = {}
          info = Hash[
            member["partcomponent"].split(",").map { |x| x.split("=") }.map { |a, b| [a, b.undump] }
          ]
          if info.keys.any? { |x| x.match?("Win32_UserAccount") }
            mi["type"] = :user
          else
            # NOTE: the type here is actually Win32_SystemAccount, because,
            # that's what groups are in the Windows universe.
            mi["type"] = :group
          end
          mi["name"] = info["Name"]
          mi
        end
      end
    end
  end
end
