module TraceView
  class SidekiqWorker
    def collect_kvs(args)
      begin
        # Attempt to collect up pertinent info.  If we hit something unexpected,
        # keep calm and instrument on.
        report_kvs = {}
        worker, msg, queue = args

        # Background Job Spec KVs
        report_kvs[:Spec] = :job
        report_kvs[:Flavor]    = :sidekiq
        report_kvs[:Queue]     = queue
        report_kvs[:Retry]     = msg['retry']
        report_kvs[:JobName]   = worker.class.to_s
        report_kvs[:MsgID]     = msg['jid']
        report_kvs[:Args]      = msg['args'].to_s[0..1024] if TV::Config[:sidekiqworker][:log_args]
        report_kvs['Backtrace'] = TV::API.backtrace        if TV::Config[:sidekiqworker][:collect_backtraces]

        # Webserver Spec KVs
        report_kvs['HTTP-Host'] = Socket.gethostname
        report_kvs[:Controller] = "Sidekiq_#{queue}"
        report_kvs[:Action] = msg['class']
        report_kvs[:URL] = "/sidekiq/#{queue}/#{msg['class'].to_s}"
      rescue => e
        TraceView.logger.warn "[traceview/sidekiq] Non-fatal error capturing KVs: #{e.message}"
      end
      report_kvs
    end

    def call(*args)
      # args: 0: worker, 1: msg, 2: queue
      result = nil
      report_kvs = collect_kvs(args)

      # Continue the trace from the enqueue side?
      incoming_context = nil
      if args[1].is_a?(Hash) && TraceView::XTrace.valid?(args[1]['X-Trace'])
        incoming_context = args[1]['X-Trace']
        report_kvs[:Async] = true
      end

      result = TraceView::API.start_trace('sidekiq-worker', incoming_context, report_kvs) do
        yield
      end

      result[0]
    end
  end
end

if defined?(::Sidekiq) && RUBY_VERSION >= '2.0' && TraceView::Config[:sidekiqworker][:enabled]
  ::TraceView.logger.info '[traceview/loading] Instrumenting sidekiq' if TraceView::Config[:verbose]

  ::Sidekiq.configure_server do |config|
    config.server_middleware do |chain|
      ::TraceView.logger.info '[traceview/loading] Adding Sidekiq worker middleware' if TraceView::Config[:verbose]
      chain.add ::TraceView::SidekiqWorker
    end
  end
end