require "uri"
require "pact_broker/hash_refinements"

module PactBroker
  module Config
    module RuntimeConfigurationLoggingMethods
      using PactBroker::HashRefinements

      module ClassMethods
        def sensitive_values(*values)
          @sensitive_values ||= []
          if values
            @sensitive_values.concat([*values])
          else
            @sensitive_values
          end
        end

        def sensitive_value?(value)
          sensitive_values.any? { |key| key == value || key == value.to_sym || key.kind_of?(Regexp) && key =~ value }
        end
      end

      module InstanceMethods
        # base_url raises a not implemented error
        def log_configuration(logger)
          source_info = to_source_trace
          attributes_to_log.collect(&:to_s).each_with_object({}) do | key, new_hash |
            new_hash[key] = {
              value: self.send(key.to_sym),
              source: source_info.dig(key, :source) || source_info.dig(key) || { type: :defaults }
            }
          end.sort_by { |key, _| key }.each { |key, value| log_config_inner(key, value, logger) }
          print_warnings(logger)
        end

        def attributes_to_log
          self.class.config_attributes - [:base_url]
        end
        private :attributes_to_log

        def log_config_inner(key, value, logger)
          # TODO fix the source display for webhook_certificates set by environment variables
          if !value.has_key? :value
            value.sort_by { |inner_key, _| inner_key }.each { |inner_key, inner_value| log_config_inner("#{key}.#{inner_key}", inner_value, logger) }
          elsif self.class.sensitive_value?(key)
            logger.info "#{key}=#{redact(key, value[:value])} source=#{value[:source]}"
          else
            logger.info "#{key}=#{value[:value].inspect} source=#{value[:source]}"
          end
        end
        private :log_config_inner

        def redact name, value
          if value && name.to_s.end_with?("_url")
            begin
              uri = URI(value)
              if uri.password
                uri.password = "*****"
                uri.to_s
              else
                value
              end
            rescue StandardError
              "*****"
            end
          elsif !value.nil?
            "*****"
          else
            nil
          end
        end
        private :redact

        def print_warnings(logger)
          if self.webhook_redact_sensitive_data == false
            logger.warn("WARNING!!! webhook_redact_sensitive_data is set to false. This will allow authentication information to be included in the webhook logs. This should only be used for debugging purposes. Do not run the application permanently in production with this value.")
          end
        end
        private :print_warnings
      end

      def self.included(receiver)
        receiver.extend         ClassMethods
        receiver.send :include, InstanceMethods
      end
    end
  end
end
