# This file is distributed under New Relic's license terms.
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
# frozen_string_literal: true

module NewRelic
  module Agent
    module Configuration
      class EnvironmentSource < DottedHash
        SUPPORTED_PREFIXES = /^new_relic_|^newrelic_/i
        SPECIAL_CASE_KEYS = [
          'NEW_RELIC_ENV', # read by NewRelic::Control::Frameworks::Ruby
          'NEW_RELIC_LOG', # read by set_log_file
          /^NEW_RELIC_METADATA_/ # read by NewRelic::Agent::Connect::RequestBuilder
        ]

        attr_accessor :alias_map, :type_map

        def initialize
          set_log_file
          set_config_file

          @alias_map = {}
          @type_map = {}

          DEFAULTS.each do |config_setting, value|
            self.type_map[config_setting] = value[:type]
            set_aliases(config_setting, value)
          end

          set_values_from_new_relic_environment_variables
        end

        def set_aliases(config_setting, value)
          set_dotted_alias(config_setting)

          return unless value[:aliases]

          value[:aliases].each do |config_alias|
            self.alias_map[config_alias] = config_setting
          end
        end

        def set_dotted_alias(original_config_setting)
          config_setting = original_config_setting.to_s

          if config_setting.include?('.')
            config_alias = config_setting.tr('.', '_').to_sym
            self.alias_map[config_alias] = original_config_setting
          end
        end

        def set_log_file
          if ENV['NEW_RELIC_LOG']
            if ENV['NEW_RELIC_LOG'].casecmp(NewRelic::STANDARD_OUT) == 0
              self[:log_file_path] = self[:log_file_name] = NewRelic::STANDARD_OUT
            else
              self[:log_file_path] = File.dirname(ENV['NEW_RELIC_LOG'])
              self[:log_file_name] = File.basename(ENV['NEW_RELIC_LOG'])
            end
          end
        end

        def set_config_file
          self[:config_path] = ENV['NRCONFIG'] if ENV['NRCONFIG']
        end

        def set_values_from_new_relic_environment_variables
          nr_env_var_keys = collect_new_relic_environment_variable_keys

          nr_env_var_keys.each do |key|
            next if SPECIAL_CASE_KEYS.any? { |pattern| pattern === key.upcase }

            set_value_from_environment_variable(key)
          end
        end

        def set_value_from_environment_variable(key)
          config_key = convert_environment_key_to_config_key(key)
          set_key_by_type(config_key, key)
        end

        def set_key_by_type(config_key, environment_key)
          value = ENV[environment_key]
          type = self.type_map[config_key]

          if type == String
            self[config_key] = value
          elsif type == Integer
            self[config_key] = value.to_i
          elsif type == Float
            self[config_key] = value.to_f
          elsif type == Symbol
            self[config_key] = value.to_sym
          elsif type == Array
            self[config_key] = if DEFAULTS[config_key].key?(:transform)
              DEFAULTS[config_key][:transform].call(value)
            else
              value.split(/\s*,\s*/)
            end
          elsif type == NewRelic::Agent::Configuration::Boolean
            if /false|off|no/i.match?(value)
              self[config_key] = false
            elsif !value.nil?
              self[config_key] = true
            end
          elsif !serverless?
            ::NewRelic::Agent.logger.info("#{environment_key} does not have a corresponding configuration setting (#{config_key} does not exist).")
            ::NewRelic::Agent.logger.info('Run `rake newrelic:config:docs` or visit https://docs.newrelic.com/docs/apm/agents/ruby-agent/configuration/ruby-agent-configuration to see a list of available configuration settings.')
            self[config_key] = value
          end
        end

        def convert_environment_key_to_config_key(key)
          stripped_key = key.gsub(SUPPORTED_PREFIXES, '').downcase.to_sym
          self.alias_map[stripped_key] || stripped_key
        end

        def collect_new_relic_environment_variable_keys
          ENV.keys.select { |key| key.match(SUPPORTED_PREFIXES) }
        end

        # we can't rely on the :'serverless_mode.enabled' config parameter being
        # set yet to signify serverless mode given that we're in the midst of
        # building the config but we can always rely on the env var being set
        # by the Lambda layer
        def serverless?
          NewRelic::Agent::ServerlessHandler.env_var_set?
        end
      end
    end
  end
end
