# Public: A singleton class to store the running configuration of
# puppet-lint.
class PuppetLint::Configuration
  # Internal: Add helper methods for a new check to the
  # PuppetLint::Configuration object.
  #
  # check - The String name of the check.
  #
  # Returns nothing.
  #
  # Signature
  #
  #   <check>_enabled?
  #   disable_<check>
  #   enable_<check>
  def self.add_check(check)
    # Public: Determine if the named check is enabled.
    #
    # Returns true if the check is enabled, otherwise return false.
    define_method(:"#{check}_enabled?") do
      settings["#{check}_disabled"] != true
    end

    # Public: Disable the named check.
    #
    # Returns nothing.
    define_method(:"disable_#{check}") do
      settings["#{check}_disabled"] = true
    end

    # Public: Enable the named check.
    #
    # Returns nothing.
    define_method(:"enable_#{check}") do
      settings["#{check}_disabled"] = false
    end
  end

  # Public: Catch situations where options are being set for the first time
  # and create the necessary methods to get & set the option in the future.
  #
  # args   - An Array of values to set the option to.
  # method - The String name of the option.
  # block  - Unused.
  #
  # Returns nothing.
  #
  # Signature
  #
  #   <option>=(value)
  def method_missing(method, *args, &_block)
    super unless method.to_s =~ %r{^(\w+)=?$}

    option = Regexp.last_match(1)
    add_option(option.to_s) if settings[option].nil?
    settings[option] = args[0] unless args.empty?
  end

  def respond_to_missing?(method, *)
    method.to_s =~ %r{^\w+=?$} || super
  end

  # Internal: Add options to the PuppetLint::Configuration object from inside
  # the class.
  #
  # option - The String name of the option.
  #
  # Returns nothing.
  #
  # Signature
  #
  #   <option>
  #   <option>=(value)
  def add_option(option)
    self.class.add_option(option)
  end

  # Public: Add an option to the PuppetLint::Configuration object from
  # outside the class.
  #
  # option - The String name of the option.
  #
  # Returns nothing.
  #
  # Signature
  #
  #   <option>
  #   <option>=(value)
  def self.add_option(option)
    # Public: Set the value of the named option.
    #
    # value - The value to set the option to.
    #
    # Returns nothing.
    define_method(:"#{option}=") do |value|
      settings[option] = value
    end

    # Public: Get the value of the named option.
    #
    # Returns the value of the option.
    define_method(option) do
      settings[option]
    end
  end

  # Internal: Register a new check.
  #
  # check - The String name of the check
  # klass - The Class containing the check logic.
  #
  # Returns nothing.
  def add_check(check, klass)
    self.class.add_check(check)
    check_object[check] = klass
  end

  # Internal: Access the internal storage for settings.
  #
  # Returns a Hash containing all the settings.
  def settings
    @settings ||= {}
  end

  # Internal: Access the internal storage for check method blocks.
  #
  # Returns a Hash containing all the check blocks.
  def check_object
    @check_object ||= {}
  end

  # Public: Get a list of all the defined checks.
  #
  # Returns an Array of String check names.
  def checks
    check_object.keys
  end

  # Public: Clear the PuppetLint::Configuration storage and set some sane
  # default values.
  #
  # Returns nothing.
  def defaults
    settings.clear
    self.with_filename = false
    self.fail_on_warnings = false
    self.error_level = :all
    self.log_format = ''
    self.with_context = false
    self.fix = false
    self.json = false
    self.sarif = false
    self.show_ignored = false
    self.ignore_paths = ['vendor/**/*.pp']
    self.github_actions = ENV.key?('GITHUB_ACTION')
    self.codeclimate_report_file = ENV.fetch('CODECLIMATE_REPORT_FILE', nil)
  end
end
