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

require 'contrast/components/interface'

module Contrast
  module Logger
    # Our decorator for the Ougai logger allowing for the logging of the
    # application environment, used to provide context during troubleshooting.
    module Application
      include Contrast::Components::Interface
      access_component :config

      ENV_KEYS = %w[HOME PWD RACK_ENV RAILS_ENV RUBY_VERSION GEM_HOME GEM_PATH].cs__freeze
      # Utility method to log some current ruby and rails information from environment
      def application_environment
        return unless info?

        info('Process environment information', p_id: Process.pid, pp_id: Process.ppid,
                                                agent_version: Contrast::Agent::VERSION)
        ENV.each do |env_key, env_value|
          env_key = env_key.to_s
          next unless ENV_KEYS.include?(env_key) ||
              (env_key.start_with?(Contrast::Components::Config::CONTRAST_ENV_MARKER) &&
                  !env_key.start_with?(Contrast::Components::Config::CONTRAST_ENV_MARKER + 'API'))

          info('Environment settings', key: env_key, value: env_value)
        end
      end

      def application_configuration
        return unless info?

        loggable = CONFIG.loggable
        info('Current configuration', configuration: loggable)
        env_keys = ENV.keys.select do |env_key|
          env_key&.to_s&.start_with?(Contrast::Components::Config::CONTRAST_ENV_MARKER)
        end
        env_items = env_keys.map { |env_key| Contrast::Utils::EnvConfigurationItem.new(env_key, nil) }
        env_translations = env_items.each_with_object({}) do |conversion, hash|
          hash[conversion.key] = conversion.dot_path_array.join('.')
        end
        info('Set by environment', overrides: env_translations)
      end

      def application_libraries
        if debug?
          log_all_libraries
        elsif info?
          log_specific_libraries
        end
      end

      FRAMEWORKS = %w[rails sinatra grape].cs__freeze
      WEB_SERVERS = %w[
        agoo falcon hoof iodine mongrel mongrel2 passenger puma rack skinny thin trinidad unicorn
        webrick yarn
      ].cs__freeze
      LIBRARIES = %w[excon json mongo moped mysql nokogiri oga ox pg psych sqlite3 typhoeus yaml].cs__freeze
      def log_specific_libraries
        FRAMEWORKS.each(&cs__method(:log_gem_data))
        WEB_SERVERS.each(&cs__method(:log_gem_data))
        LIBRARIES.each(&cs__method(:log_gem_data))
      end

      private

      def log_all_libraries
        return unless debug?

        Gem.loaded_specs.each_pair do |_name, gem_spec|
          debug('Gem loaded',
                # rubocop:disable Security/Module/Name -- gems builtin.
                gem_name: gem_spec.name,
                gem_version: gem_spec.version.to_s)
        end
      end

      def log_gem_data gem_name
        gem_spec = Gem.loaded_specs[gem_name]
        return unless gem_spec

        info('Gem loaded', gem_name: gem_spec.name, gem_version: gem_spec.version.to_s)
        # rubocop:enable Security/Module/Name
      end
    end
  end
end