# This file is distributed under New Relic's license terms.
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
# frozen_string_literal: true

require 'rack/test'
require 'new_relic/rack/agent_hooks'
require './testing_app'

class SyntheticsTest < Minitest::Test
  include MultiverseHelpers
  include Rack::Test::Methods

  setup_and_teardown_agent

  def app
    Rack::Builder.app { run(TestingApp.new) }
  end

  def last_sent_analytics_event
    call = first_call_for(:analytic_event_data)

    events = call.events

    assert_equal(1, events.size)
    events.first
  end

  def last_sent_transaction_trace
    call = first_call_for(:transaction_sample_data)

    traces = call.samples

    assert_equal(1, traces.size)
    traces.first
  end

  def generate_synthetics_header(test)
    synthetics_key = test['settings']['syntheticsEncodingKey']
    synthetics_payload = test['inputHeaderPayload']

    return nil if synthetics_payload.empty?

    encoded_synthetics_payload = ::JSON.dump(synthetics_payload)
    obfuscated_synthetics_header = ::NewRelic::Agent::Obfuscator.new(synthetics_key).obfuscate(encoded_synthetics_payload)

    assert_equal(test['inputObfuscatedHeader']['X-NewRelic-Synthetics'], obfuscated_synthetics_header)
    obfuscated_synthetics_header
  end

  def request_headers_for_test(test)
    header_value = generate_synthetics_header(test)
    if header_value
      {'HTTP_X_NEWRELIC_SYNTHETICS' => header_value}
    else
      {}
    end
  end

  def request_params_for_test(test)
    if test['settings']
      {'guid' => test['settings']['transactionGuid']}
    else
      {}
    end
  end

  def validate_transaction_event(event, test)
    event_spec = test['outputTransactionEvent']
    expected_event_attrs = event_spec['expectedAttributes']
    non_expected_event_attrs = event_spec['nonExpectedAttributes']

    expected_event_attrs.each do |key, value|
      msg = "Incorrect value for analytic event key '#{key}'. Full event = #{event.inspect}"
      assert_equal(value, event[0][key], msg) unless key == 'nr.guid' && event[0][key].nil?
    end

    non_expected_event_attrs.each do |key|
      msg = "Did not expect key '#{key}' on analytics event. Actual value was #{event[0][key]}"

      refute_includes(event[0].keys, key, msg)
    end
  end

  def validate_transaction_trace(trace, test)
    trace_spec = test['outputTransactionTrace']
    expected_resource_id = trace_spec['header']['field_9']
    expected_attrs = trace_spec['expectedIntrinsics']
    non_expected_attrs = trace_spec['nonExpectedIntrinsics']

    trace_attrs = trace.intrinsic_attributes

    assert_equal(expected_resource_id, trace.synthetics_resource_id)

    expected_attrs.each do |key, value|
      key = "#{key}"
      msg = "Incorrect value for transaction trace intrinsic '#{key}'. All intrinsics = #{trace_attrs.inspect}"

      assert_equal(value, trace_attrs[key], msg)
    end

    non_expected_attrs.each do |key|
      key = "#{key}"
      msg = "Did not expect key '#{key}' on transaction trace. Actual value was #{trace_attrs[key]}"

      refute_includes(trace_attrs.keys, key, msg)
    end
  end

  # These tests do *not* cover passing on the correct synthetics header to
  # outgoing HTTP requests, since testing that requires our various HTTP client
  # libraries to be present. That aspect is tested in http_client_test_cases.rb
  load_cross_agent_test('synthetics/synthetics').each do |test|
    define_method("test_synthetics_#{test['name']}") do
      config = {
        :encoding_key => test['settings']['agentEncodingKey'],
        :trusted_account_ids => test['settings']['trustedAccountIds'],
        :'transaction_tracer.transaction_threshold' => 0.0
      }

      with_config(config) do
        NewRelic::Agent.instance.events.notify(:initial_configuration_complete)

        get '/', request_params_for_test(test), request_headers_for_test(test)

        NewRelic::Agent.agent.send(:transmit_data)
        NewRelic::Agent.agent.send(:harvest_and_send_analytic_event_data)

        event = last_sent_analytics_event
        validate_transaction_event(event, test)

        trace = last_sent_transaction_trace
        validate_transaction_trace(trace, test)
      end
    end
  end
end
