# it might be desirable to parse the Puppetfile as a string instead of evaling it. 
# this module allows you to do just that.
require 'ra10ke/monkey_patches'

module Ra10ke
    module PuppetfileParser

      # @return [Array] - returns a array of hashes that contain modules with a git source
      def git_modules(file = puppetfile)
        modules(file).find_all do |mod|
          mod[:args].key?(:git)
        end
      end

      # @return [Array] - returns a array of hashes that contain modules from the Forge
      def forge_modules(file = puppetfile)
        modules(file).reject do |mod|
          mod[:args].key?(:git)
        end
      end

      # @param puppetfile [String] - the absolute path to the puppetfile
      # @return [Array] - returns an array of module hashes that represent the puppetfile
      # @example
      # [{:namespace=>"puppetlabs", :name=>"stdlib", :args=>[]},
      # {:namespace=>"petems", :name=>"swap_file", :args=>["'4.0.0'"]}]
      def modules(puppetfile)
        @modules ||= begin
          return [] unless File.exist?(puppetfile)

          all_lines = File.read(puppetfile).lines.map(&:strip_comment)
          # remove comments from all the lines
          lines_without_comments = all_lines.reject { |line| line.match(/#.*\n/) }.join("\n")
          lines_without_comments.split(/^mod/).map do |line|
            next nil if line =~ /^forge/
            next nil if line.empty?

            parse_module_args(line)
          end.compact.uniq
        end
      end

      # @param data [String] - the string to parse the puppetfile args out of
      # @return [Array] -  an array of arguments in hash form
      # @example
      # {:namespace=>"puppetlabs", :name=>"stdlib", :args=>[]}
      # {:namespace=>"petems", :name=>"swap_file", :args=>["'4.0.0'"]}
      def parse_module_args(data)
        return {} if data.empty?
        args = data.split(',').map(&:strip)
        # we can't guarantee that there will be a namespace when git is used
        # remove quotes and dash and slash
        namespace, name = args.shift.gsub(/'|"/, '').split(%r{-|/})
        name ||= namespace
        namespace = nil if namespace == name
        {
          namespace: namespace,
          name: name,
          args: process_args(args)
        }
      end

      # @return [Array] - returns an array of hashes with the args in key value pairs
      # @param [Array] - the arguments processed from each entry in the puppetfile
      # @example
      # [{:args=>[], :name=>"razor", :namespace=>"puppetlabs"},
      #  {:args=>[{:version=>"0.0.3"}], :name=>"ntp", :namespace=>"puppetlabs"},
      #  {:args=>[], :name=>"inifile", :namespace=>"puppetlabs"},
      #  {:args=>
      #    [{:git=>"https://github.com/nwops/reportslack.git"}, {:ref=>"1.0.20"}],
      #   :name=>"reportslack",
      #   :namespace=>"nwops"},
      #  {:args=>{:git=>"git://github.com/puppetlabs/puppetlabs-apt.git"},
      #   :name=>"apt",
      #   :namespace=>nil}
      # ]
      def process_args(args)
        results = {}
        args.each do |arg|
          a = arg.gsub(/'|"/, '').split(/\A\:|\:\s|\=\>/).map(&:strip).reject(&:empty?)
          if a.count < 2
            results[:version] = a.first
          else
            results[a.first.to_sym] = a.last
          end
        end
        results
      end
    end
end
