# frozen_string_literal: true

module DatadogHelpers
  DATADOG_VERSION = defined?(DDTrace) ? DDTrace::VERSION : Datadog::VERSION
  ERROR_TAG = if Gem::Version.new(DATADOG_VERSION::STRING) >= Gem::Version.new("1.8.0")
    "error.message"
  else
    "error.msg"
  end

  private

  def verify_instrumented_request(status, verb:, uri:, span: fetch_spans.first, service: datadog_service_name.to_s, error: nil)
    if Gem::Version.new(DATADOG_VERSION::STRING) >= Gem::Version.new("2.0.0")
      assert span.type == "http"
    else
      assert span.span_type == "http"
    end
    assert span.name == "#{datadog_service_name}.request"
    assert span.service == service

    assert span.get_tag("out.host") == uri.host
    assert span.get_tag("out.port") == 80
    assert span.get_tag("http.method") == verb
    assert span.get_tag("http.url") == uri.path

    if status && status >= 400
      verify_http_error_span(span, status, error)
    elsif error
      verify_error_span(span)
    else
      assert span.status.zero?
      assert span.get_tag("http.status_code") == status.to_s
      # peer service
      # assert span.get_tag("peer.service") == span.service
    end
  end

  def verify_http_error_span(span, status, error)
    assert span.get_tag("http.status_code") == status.to_s
    assert span.get_tag("error.type") == error
    assert !span.get_tag(ERROR_TAG).nil?
    assert span.status == 1
  end

  def verify_error_span(span)
    assert span.get_tag("error.type") == "HTTPX::NativeResolveError"
    assert !span.get_tag(ERROR_TAG).nil?
    assert span.status == 1
  end

  def verify_no_distributed_headers(request_headers)
    assert !request_headers.key?("x-datadog-parent-id")
    assert !request_headers.key?("x-datadog-trace-id")
    assert !request_headers.key?("x-datadog-sampling-priority")
  end

  def verify_distributed_headers(request_headers, span: fetch_spans.first, sampling_priority: 1)
    if Gem::Version.new(DATADOG_VERSION::STRING) >= Gem::Version.new("2.0.0")
      assert request_headers["x-datadog-parent-id"] == span.id.to_s
    else
      assert request_headers["x-datadog-parent-id"] == span.span_id.to_s
    end
    assert request_headers["x-datadog-trace-id"] == trace_id(span)
    assert request_headers["x-datadog-sampling-priority"] == sampling_priority.to_s
  end

  if Gem::Version.new(DATADOG_VERSION::STRING) >= Gem::Version.new("1.17.0")
    def trace_id(span)
      Datadog::Tracing::Utils::TraceId.to_low_order(span.trace_id).to_s
    end
  else
    def trace_id(span)
      span.trace_id.to_s
    end
  end

  def verify_analytics_headers(span, sample_rate: nil)
    assert span.get_metric("_dd1.sr.eausr") == sample_rate
  end

  def set_datadog(options = {}, &blk)
    Datadog.configure do |c|
      c.tracing.instrument(datadog_service_name, options, &blk)
    end

    tracer # initialize tracer patches
  end

  def tracer
    @tracer ||= begin
      tr =  Datadog::Tracing.send(:tracer)
      def tr.write(trace)
        @traces ||= []
        @traces << trace
      end
      tr
    end
  end

  def trace_with_sampling_priority(priority)
    tracer.trace("foo.bar") do
      tracer.active_trace.sampling_priority = priority
      yield
    end
  end

  # Returns spans and caches it (similar to +let(:spans)+).
  def spans
    @spans ||= fetch_spans
  end

  # Retrieves and sorts all spans in the current tracer instance.
  # This method does not cache its results.
  def fetch_spans
    spans = (tracer.instance_variable_get(:@traces) || []).map(&:spans)
    spans.flatten.sort! do |a, b|
      if a.name == b.name
        if a.resource == b.resource
          if a.start_time == b.start_time
            a.end_time <=> b.end_time
          else
            a.start_time <=> b.start_time
          end
        else
          a.resource <=> b.resource
        end
      else
        a.name <=> b.name
      end
    end
  end
end
