# frozen_string_literal: true

module Facter
  module OptionsValidator
    INVALID_PAIRS_RULES = { '--color' => '--no-color',
                            '--json' => ['--no-json', '-y', '--yaml', '--hocon'],
                            '--yaml' => ['--no-yaml', '-j', '--hocon'],
                            '--hocon' => '--no-hocon',
                            '-j' => ['--no-json', '--hocon'],
                            '-y' => ['--no-yaml', '-j', '--hocon'],
                            '--puppet' => ['--no-puppet', '--no-ruby', '--no-custom-facts'],
                            '-p' => ['--no-puppet', '--no-ruby', '--no-custom-facts'],
                            '--no-external-facts' => '--external-dir',
                            '--custom-dir' => ['--no-custom-facts', '--no-ruby'] }.freeze
    DUPLICATED_OPTIONS_RULES = { '-j' => '--json', '-y' => '--yaml', '-p' => '--puppet', '-h' => '--help',
                                 '-v' => '--version', '-l' => '--log-level', '-d' => '--debug',
                                 '-c' => '--config' }.freeze
    LOG_LEVEL = %i[none trace debug info warn error fatal].freeze

    def self.validate(options)
      DUPLICATED_OPTIONS_RULES.each do |key, value|
        if options.include?(key) && options.include?(value)
          write_error_and_exit("option #{value} cannot be specified more than once.")
        end
      end

      INVALID_PAIRS_RULES.each do |key, value|
        common_values = [value].flatten & options
        if options.include?(key) && common_values.any?
          write_error_and_exit("#{key} and #{common_values.first} options conflict: please specify only one.")
        end
      end
    end

    def self.write_error_and_exit(message)
      log = Facter::Log.new(self)
      log.error(message, true)
      Facter::Cli.start(['--help'])

      exit 1
    end

    def self.validate_configs(options)
      conflicting_configs(options).each do |op|
        next unless op.values[0] && op.values[1]

        message = "#{op.keys[0]} and #{op.keys[1]} options conflict: please specify only one"
        write_error_and_exit(message)
      end
      validate_log_options(options)
    end

    def self.conflicting_configs(options)
      no_ruby = !options[:ruby]
      no_custom_facts = !options[:custom_facts]
      puppet = options[:puppet]
      custom_dir = options[:custom_dir].nil? ? false : options[:custom_dir].any?
      default_external_dir = Facter::OptionStore.default_external_dir
      # --puppet/-p option adds an external directory and is not an explicitly
      # set external_dir from cli or config file
      default_external_dir += [Puppet[:pluginfactdest]] if puppet && const_defined?('Puppet')
      external_dir = if options[:external_dir].nil? || options[:external_dir].none?
                       false
                     else
                       options[:external_dir] != default_external_dir
                     end
      [
        { 'no-ruby' => no_ruby, 'custom-dir' => custom_dir },
        { 'no-external-facts' => !options[:external_facts], 'external-dir' => external_dir },
        { 'no-custom-facts' => no_custom_facts, 'custom-dir' => custom_dir },
        { 'no-ruby' => no_ruby, 'puppet' => puppet },
        { 'no-custom-facts' => no_custom_facts, 'puppet' => puppet }
      ]
    end

    def self.validate_log_options(options)
      # TODO: find a better way to do this
      return if options[:debug] == true && options[:log_level] == :debug
      return if options[:verbose] == true && options[:log_level] == :info

      return unless [options[:debug],
                     options[:verbose],
                     options[:log_level] != Facter::DEFAULT_LOG_LEVEL]
                    .count(true) > 1

      message = 'debug, verbose, and log-level options conflict: please specify only one.'
      write_error_and_exit(message)
    end
  end
end
