# frozen_string_literal: true

require 'http/tracer/version'
require 'opentracing'

module HTTP
  module Tracer
    class << self
      attr_accessor :ignore_request, :tracer

      IngoreRequest = ->(_request, _options) { false }

      def instrument(tracer: OpenTracing.global_tracer, ignore_request: IngoreRequest)
        begin
          require 'http'
        rescue LoadError
          return
        end
        raise IncompatibleGemVersion unless compatible_version?

        @ignore_request = ignore_request
        @tracer = tracer
        patch_perform
      end

      def compatible_version?
        Gem::Version.new(HTTP::VERSION) >= Gem::Version.new("0.1.0")
      end

      def remove
        return unless ::HTTP::Client.method_defined?(:perform_without_tracing)

        ::HTTP::Client.class_eval do
          remove_method :perform
          alias_method :perform, :perform_without_tracing
          remove_method :perform_without_tracing
        end
      end

      def patch_perform
        ::HTTP::Client.class_eval do
          def perform_with_tracing(request, options)
            if ::HTTP::Tracer.ignore_request.call(request, options)
              res = perform_without_tracing(request, options)
            else
              path, host, port, verb = nil
              path = request.uri.path if request.uri.respond_to?(:path)
              host = request.uri.host if request.uri.respond_to?(:host)
              port = request.uri.port if request.uri.respond_to?(:port)
              verb = request.verb.to_s.upcase if request.respond_to?(:verb)

              tags = {
                'component' => 'ruby-httprb',
                'span.kind' => 'client',
                'http.method' => verb,
                'http.url' => path,
                'peer.host' => host,
                'peer.port' => port
              }.compact

              tracer = ::HTTP::Tracer.tracer

              tracer.start_active_span('http.request', tags: tags) do |scope|
                OpenTracing.inject(scope.span.context, OpenTracing::FORMAT_RACK, options.headers)

                res = perform_without_tracing(request, options)

                scope.span.set_tag('http.status_code', res.status)
                scope.span.set_tag('error', true) if res.is_a?(StandardError)
              end
            end

            res
          end

          alias perform_without_tracing perform
          alias perform perform_with_tracing
        end
      end
    end
  end
end