# frozen_string_literal: true

begin
  # upcoming 2.0
  require "datadog"
rescue LoadError
  require "ddtrace"
end

require "test_helper"
require "support/http_helpers"
require "httpx/adapters/datadog"
require_relative "datadog_helpers"

class DatadogTest < Minitest::Test
  include HTTPHelpers
  include DatadogHelpers

  def test_datadog_successful_get_request
    set_datadog
    uri = URI(build_uri("/get", "http://#{httpbin}"))

    response = HTTPX.get(uri)
    verify_status(response, 200)

    assert !fetch_spans.empty?, "expected to have spans"
    verify_instrumented_request(response.status, verb: "GET", uri: uri)
    verify_distributed_headers(request_headers(response))
  end

  def test_datadog_successful_post_request
    set_datadog
    uri = URI(build_uri("/post", "http://#{httpbin}"))

    response = HTTPX.post(uri, body: "bla")
    verify_status(response, 200)

    assert !fetch_spans.empty?, "expected to have spans"
    verify_instrumented_request(response.status, verb: "POST", uri: uri)
    verify_distributed_headers(request_headers(response))
  end

  def test_datadog_successful_multiple_requests
    set_datadog
    get_uri = URI(build_uri("/get", "http://#{httpbin}"))
    post_uri = URI(build_uri("/post", "http://#{httpbin}"))

    get_response, post_response = HTTPX.request([["GET", get_uri], ["POST", post_uri]])
    verify_status(get_response, 200)
    verify_status(post_response, 200)

    assert fetch_spans.size == 2, "expected to have 2 spans"
    get_span, post_span = fetch_spans
    verify_instrumented_request(get_response.status, span: get_span, verb: "GET", uri: get_uri)
    verify_instrumented_request(post_response.status, span: post_span, verb: "POST", uri: post_uri)
    verify_distributed_headers(request_headers(get_response), span: get_span)
    verify_distributed_headers(request_headers(post_response), span: post_span)
    verify_analytics_headers(get_span)
    verify_analytics_headers(post_span)
  end

  def test_datadog_server_error_request
    set_datadog
    uri = URI(build_uri("/status/500", "http://#{httpbin}"))

    response = HTTPX.get(uri)
    verify_status(response, 500)

    assert !fetch_spans.empty?, "expected to have spans"
    verify_instrumented_request(response.status, verb: "GET", uri: uri, error: "HTTPX::HTTPError")
  end

  def test_datadog_client_error_request
    set_datadog
    uri = URI(build_uri("/status/404", "http://#{httpbin}"))

    response = HTTPX.get(uri)
    verify_status(response, 404)

    assert !fetch_spans.empty?, "expected to have spans"
    verify_instrumented_request(response.status, verb: "GET", uri: uri, error: "HTTPX::HTTPError")
  end

  def test_datadog_some_other_error
    set_datadog
    uri = URI("http://unexisting/")

    response = HTTPX.get(uri)
    assert response.is_a?(HTTPX::ErrorResponse), "response should contain errors"

    assert !fetch_spans.empty?, "expected to have spans"
    verify_instrumented_request(nil, verb: "GET", uri: uri, error: "HTTPX::NativeResolveError")
  end

  def test_datadog_host_config
    uri = URI(build_uri("/get", "http://#{httpbin}"))
    set_datadog(describe: /#{uri.host}/) do |http|
      http.service_name = "httpbin"
      http.split_by_domain = false
    end

    response = HTTPX.get(uri)
    verify_status(response, 200)

    assert !fetch_spans.empty?, "expected to have spans"
    verify_instrumented_request(response.status, service: "httpbin", verb: "GET", uri: uri)
    verify_distributed_headers(request_headers(response))
  end

  def test_datadog_split_by_domain
    uri = URI(build_uri("/get", "http://#{httpbin}"))
    set_datadog do |http|
      http.split_by_domain = true
    end

    response = HTTPX.get(uri)
    verify_status(response, 200)

    assert !fetch_spans.empty?, "expected to have spans"
    verify_instrumented_request(response.status, service: uri.host, verb: "GET", uri: uri)
    verify_distributed_headers(request_headers(response))
  end

  def test_datadog_distributed_headers_disabled
    set_datadog(distributed_tracing: false)
    uri = URI(build_uri("/get", "http://#{httpbin}"))

    sampling_priority = 10
    response = trace_with_sampling_priority(sampling_priority) do
      HTTPX.get(uri)
    end
    verify_status(response, 200)

    assert !fetch_spans.empty?, "expected to have spans"
    span = fetch_spans.last
    verify_instrumented_request(response.status, span: span, verb: "GET", uri: uri)
    verify_no_distributed_headers(request_headers(response))
    verify_analytics_headers(span)
  end

  def test_datadog_distributed_headers_sampling_priority
    set_datadog
    uri = URI(build_uri("/get", "http://#{httpbin}"))

    sampling_priority = 10
    response = trace_with_sampling_priority(sampling_priority) do
      HTTPX.get(uri)
    end

    verify_status(response, 200)

    assert !fetch_spans.empty?, "expected to have spans"
    span = fetch_spans.last
    verify_instrumented_request(response.status, span: span, verb: "GET", uri: uri)
    verify_distributed_headers(request_headers(response), span: span, sampling_priority: sampling_priority)
    verify_analytics_headers(span)
  end

  def test_datadog_analytics_enabled
    set_datadog(analytics_enabled: true)
    uri = URI(build_uri("/get", "http://#{httpbin}"))

    response = HTTPX.get(uri)
    verify_status(response, 200)

    assert !fetch_spans.empty?, "expected to have spans"
    span = fetch_spans.last
    verify_instrumented_request(response.status, span: span, verb: "GET", uri: uri)
    verify_analytics_headers(span, sample_rate: 1.0)
  end

  def test_datadog_analytics_sample_rate
    set_datadog(analytics_enabled: true, analytics_sample_rate: 0.5)
    uri = URI(build_uri("/get", "http://#{httpbin}"))

    response = HTTPX.get(uri)
    verify_status(response, 200)

    assert !fetch_spans.empty?, "expected to have spans"
    span = fetch_spans.last
    verify_instrumented_request(response.status, span: span, verb: "GET", uri: uri)
    verify_analytics_headers(span, sample_rate: 0.5)
  end

  def test_datadog_per_request_span_with_retries
    set_datadog
    uri = URI(build_uri("/status/404", "http://#{httpbin}"))

    http = HTTPX.plugin(:retries, max_retries: 2, retry_on: ->(r) { r.status == 404 })
    response = http.get(uri)
    verify_status(response, 404)

    assert fetch_spans.size == 3, "expected to 3 spans"
    fetch_spans.each do |span|
      verify_instrumented_request(response.status, span: span, verb: "GET", uri: uri, error: "HTTPX::HTTPError")
    end
  end

  private

  def setup
    super
    Datadog.registry[:httpx].reset_configuration!
  end

  def teardown
    super
    Datadog.registry[:httpx].reset_configuration!
    Datadog.configuration.tracing[:httpx].enabled = false
  end

  def datadog_service_name
    :httpx
  end

  def request_headers(response)
    body = json_body(response)
    body["headers"].transform_keys(&:downcase)
  end
end
