# frozen_string_literal: true

require 'getaround_utils/ougai/json_formatter'
require 'request_store'
require 'rails/railtie'
require 'ougai'

module GetaroundUtils; end

module GetaroundUtils::Railties; end

# Rails-compatible Ougai Logger
# https://github.com/tilfin/ougai/wiki/Use-as-Rails-logger#define-a-custom-logger
class OugaiRailsLogger < Ougai::Logger
  include ActiveSupport::LoggerThreadSafeLevel
  include ActiveSupport::LoggerSilence

  def initialize(*args)
    super
    after_initialize if respond_to?(:after_initialize) && ActiveSupport::VERSION::MAJOR < 6
  end
end

# Patch for ActiveSupport::TaggedLogging
# https://github.com/tilfin/ougai/wiki/Use-as-Rails-logger#with-activesupporttaggedlogging
module OugaiTaggedLoggingFormatter
  def call(severity, time, progname, data)
    if is_a?(Ougai::Formatters::Base)
      data = { msg: data.to_s } unless data.is_a?(Hash)
      data[:tags] = current_tags if current_tags.any?
      _call(severity, time, progname, data)
    else
      super
    end
  end
end

# Custom middleware to persist request metadatas
# https://github.com/tilfin/ougai/issues/73#issuecomment-475866224
# https://github.com/tilfin/ougai/issues/107#issuecomment-636050223
class OugaiRequestStoreMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    RequestStore.store[:ougai] = { http: { request_id: env['action_dispatch.request_id'] } }
    @app.call(env)
  end
end

class GetaroundUtils::Railties::Ougai < Rails::Railtie
  config.ougai_logger = OugaiRailsLogger.new($stdout)
  config.ougai_logger.formatter = GetaroundUtils::Ougai::JsonFormatter.new

  config.ougai_logger.before_log = lambda do |data|
    data.deep_merge!(RequestStore.store[:ougai]) \
      if defined?(RequestStore) && RequestStore.store.key?(:ougai)

    data.merge!(sidekiq: Thread.current[:sidekiq_context]) \
      if defined?(Sidekiq) && Thread.current.key?(:sidekiq_context)
  end

  initializer :getaround_utils_ougai, before: :initialize_logger do |app|
    app.config.logger = config.ougai_logger
  end

  initializer :getaround_utils_ougai_middleware do |app|
    app.config.app_middleware.insert_after(ActionDispatch::RequestId, OugaiRequestStoreMiddleware)
  end

  initializer :getaround_utils_ougai_activesupport do
    ActiveSupport::TaggedLogging::Formatter.prepend(OugaiTaggedLoggingFormatter)
  end

  initializer :getaround_utils_ougai_lograge do |app|
    next unless defined?(Lograge)

    # https://github.com/tilfin/ougai/wiki/Use-as-Rails-logger#with-lograge
    app.config.lograge.logger = app.config.logger
    app.config.lograge.formatter = Lograge::Formatters::Raw.new
  end

  initializer :getaround_utils_ougai_sidekiq do
    next unless defined?(Sidekiq)

    # https://github.com/tilfin/ougai/wiki/Customize-Sidekiq-logger
    Sidekiq.configure_client do |config|
      config.logger = Rails.application.config.logger
    end

    Sidekiq.configure_server do |config|
      config.logger = Rails.application.config.logger

      original_handler = config.error_handlers.shift
      config.error_handlers << lambda do |ex, ctx|
        if Sidekiq.logger.is_a?(Ougai::Logger)
          Sidekiq.logger.warn(ex, job: ctx[:job])
        else
          original_handler.call(ex, ctx)
        end
      end
    end
  end
end