# 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

module NewRelic::Agent::Instrumentation
  module OpenSearch
    PRODUCT_NAME = 'OpenSearch'
    OPERATION = 'perform_request'
    OPERATION_PATTERN = %r{/lib/opensearch/api/(?!.+#{OPERATION})}
    INSTANCE_METHOD_PATTERN = /:in (?:`|')(?:.+#)?([^']+)'\z/
    INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)

    def perform_request_with_tracing(_method, _path, params = NewRelic::EMPTY_HASH, body = nil, _headers = nil, _opts = nil, &_block)
      return yield unless NewRelic::Agent::Tracer.tracing_enabled?

      segment = NewRelic::Agent::Tracer.start_datastore_segment(
        product: PRODUCT_NAME,
        operation: nr_operation || OPERATION,
        host: nr_hosts[:host],
        port_path_or_id: nr_hosts[:port],
        database_name: nr_cluster_name
      )
      begin
        NewRelic::Agent::Tracer.capture_segment_error(segment) { yield }
      ensure
        if segment
          segment.notice_nosql_statement(nr_reported_query(body || params))
          segment.finish
        end
      end
    end

    private

    # See Elasticsearch instrumentation for explanation on Ruby 3.4 changes to match instance method
    def nr_operation
      location = caller_locations.detect { |loc| loc.to_s.match?(OPERATION_PATTERN) }
      return unless location && location.to_s =~ INSTANCE_METHOD_PATTERN

      Regexp.last_match(1)
    end

    def nr_reported_query(query)
      return unless NewRelic::Agent.config[:'opensearch.capture_queries']
      return query unless NewRelic::Agent.config[:'opensearch.obfuscate_queries']

      NewRelic::Agent::Datastores::NosqlObfuscator.obfuscate_statement(query)
    end

    def nr_cluster_name
      return @nr_cluster_name if defined?(@nr_cluster_name)
      return if nr_hosts.empty?

      NewRelic::Agent.disable_all_tracing do
        @nr_cluster_name ||= perform_request('GET', '/').body['cluster_name']
      end
    rescue StandardError => e
      NewRelic::Agent.logger.error('Failed to get cluster name for OpenSearch', e)
      nil
    end

    def nr_hosts
      @nr_hosts ||= (transport.hosts.first || NewRelic::EMPTY_HASH)
    end
  end
end
