# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
# frozen_string_literal: true

require 'contrast/utils/object_share'

module Contrast
  module Agent
    module DiagnosticsConfig
      # Diagnostics tools to be included in config components.
      module DiagnosticsTools
        CHECK = 'd'

        # Converts current configuration for array of values to effective config values class and appends them to
        # EffectiveConfig class. Must be used inside Config Components only.
        #
        # @param effective_config [Contrast::Agent::DiagnosticsConfig::EffectiveConfig]
        # @param config_values [Array<String>] array of the names of values.
        # @param canonical_prefix [String] starting of the path to config => api.proxy...
        # @param name_prefix [String] the name of the config prefix => contrast.api_key, contrast.url
        def add_effective_config_values effective_config, config_values, canonical_prefix, name_prefix
          return if config_values.to_s.empty?

          config_values.each do |config|
            Contrast::Agent::DiagnosticsConfig::EffectiveConfigValue.new.tap do |value|
              next if (config_val = send(config.to_sym)).to_s.empty?

              config_name = assign_name(config)
              value.canonical_name = "#{ canonical_prefix }.#{ config_name }"
              value.name = "#{ name_prefix }.#{ config_name }"
              value.value = config_val
              value.source = Contrast::CONFIG.sources.get(value.canonical_name)
              if value.source == Contrast::Components::Config::Sources::YAML
                value.filename = Contrast::CONFIG.config_file_path
              end
              effective_config.values << value
            rescue StandardError => e
              log_error(e)
              next
            end
          end
        end

        # Converts current configuration for single value to effective config values class and appends them to
        # EffectiveConfig class. Must be used inside Config Components only.
        #
        # @param effective_config [Contrast::Agent::DiagnosticsConfig::EffectiveConfig]
        # @param config_name [String] name of the config.
        # @param config_value [String, Boolean] value of the config.
        # @param canonical_prefix [String] starting of the path to config => api.proxy...
        # @param name_prefix [String] the name of the config prefix => contrast.api_key, contrast.url
        def add_single_effective_value effective_config, config_name, config_value, canonical_prefix, name_prefix
          Contrast::Agent::DiagnosticsConfig::EffectiveConfigValue.new.tap do |value|
            break if config_value.to_s.empty?

            value.value = config_value
            value.canonical_name = "#{ canonical_prefix }.#{ config_name }"
            value.name = "#{ name_prefix }.#{ config_name }"
            value.source = Contrast::CONFIG.sources.get(value.canonical_name)
            if value.source == Contrast::Components::Config::Sources::YAML
              value.filename = Contrast::CONFIG.config_file_path
            end
            effective_config.values << value
          rescue StandardError => e
            log_error(e)
            next
          end
        end

        private

        # Assigns a proper name for the config removing '?' out of method names.
        #
        # @param config [String] name of the configuration
        # @return [String]
        def assign_name config
          return Contrast::Utils::ObjectShare::EMPTY_STRING unless config

          name = config.dup
          if name.end_with?(Contrast::Utils::ObjectShare::QUESTION_MARK)
            # check and remove '?' : start_bundled_service? => start_bundled_service
            name.delete!(Contrast::Utils::ObjectShare::QUESTION_MARK)
            name.chop! if name.end_with?(CHECK)
            name
          end
          name
        end

        # Logs any caught error.
        #
        # @param error [StandardError]
        def log_error error
          Contrast::CONFIG.proto_logger.warn('Could not write effective config to file: ',
                                             error: error, backtrace: error.backtrace)
        end
      end
    end
  end
end