# frozen_string_literal: true

# Copyright (C) 2020 MongoDB Inc.
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an 'AS IS' BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

module Mongo
  # @api private
  module Utils
    class LocalLogger
      include Loggable

      def initialize(**opts)
        @options = opts
      end

      attr_reader :options
    end

    # @option opts [ true | false | nil | Integer ] :bg_error_backtrace
    #   Experimental. Set to true to log complete backtraces for errors in
    #   background threads. Set to false or nil to not log backtraces. Provide
    #   a positive integer to log up to that many backtrace lines.
    # @option opts [ Logger ] :logger A custom logger to use.
    # @option opts [ String ] :log_prefix A custom log prefix to use when
    #   logging.
    module_function def warn_bg_exception(msg, exc, **opts)
      bt_excerpt = excerpt_backtrace(exc, **opts)
      logger = LocalLogger.new(**opts)
      logger.log_warn("#{msg}: #{exc.class}: #{exc}#{bt_excerpt}")
    end

    # @option opts [ true | false | nil | Integer ] :bg_error_backtrace
    #   Experimental. Set to true to log complete backtraces for errors in
    #   background threads. Set to false or nil to not log backtraces. Provide
    #   a positive integer to log up to that many backtrace lines.
    module_function def excerpt_backtrace(exc, **opts)
      case lines = opts[:bg_error_backtrace]
      when Integer
        ":\n#{exc.backtrace[0..lines].join("\n")}"
      when false, nil
        nil
      else
        ":\n#{exc.backtrace.join("\n")}"
      end
    end

    # Symbolizes the keys in the provided hash.
    module_function def shallow_symbolize_keys(hash)
      Hash[hash.map { |k, v| [ k.to_sym, v ] }]
    end

    # Stringifies the keys in the provided hash and converts underscore
    # style keys to camel case style keys.
    module_function def shallow_camelize_keys(hash)
      Hash[hash.map { |k, v| [ camelize(k), v ] }]
    end

    module_function def camelize(sym)
      sym.to_s.gsub(/_(\w)/) { ::Regexp.last_match(1).upcase }
    end

    # @note server_api must have symbol keys or be a BSON::Document.
    module_function def transform_server_api(server_api)
      {}.tap do |doc|
        if (version = server_api[:version])
          doc['apiVersion'] = version
        end
        doc['apiStrict'] = server_api[:strict] unless server_api[:strict].nil?
        doc['apiDeprecationErrors'] = server_api[:deprecation_errors] unless server_api[:deprecation_errors].nil?
      end
    end

    # This function should be used if you need to measure time.
    # @example Calculate elapsed time.
    #   starting = Utils.monotonic_time
    #   # do something time consuming
    #   ending = Utils.monotonic_time
    #   puts "It took #{(ending - starting).to_i} seconds"
    #
    # @see https://blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way/
    #
    # @return [Float] seconds according to monotonic clock
    module_function def monotonic_time
      Process.clock_gettime(Process::CLOCK_MONOTONIC)
    end
  end
end
