# 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
  module Agent
    module HTTPClients
      MUST_IMPLEMENT_ERROR = 'Subclasses of %s must implement a :%s method'
      WHINY_NIL_ERROR = '%s cannot initialize with a nil wrapped_response object.'

      # This class provides a public interface for wrapping HTTP requests. This
      # may be used to create wrappers that are compatible with New Relic's
      # external request API.
      #
      # @api public
      class AbstractRequest
        LHOST = 'host'
        UHOST = 'Host'
        COLON = ':'

        %i[[] []= type host_from_header host method headers uri].each do |name|
          define_method(name) do
            not_implemented(name)
          end
        end

        private

        def not_implemented(method_name)
          raise NotImplementedError, MUST_IMPLEMENT_ERROR % [self.class, method_name]
        end
      end

      # This class implements the adaptor pattern and is used internally provide
      # uniform access to the underlying HTTP Client's response object
      # NOTE: response_object should be non-nil!
      class AbstractResponse # :nodoc:
        def initialize(wrapped_response)
          if wrapped_response.nil?
            raise ArgumentError, WHINY_NIL_ERROR % self.class
          end

          @wrapped_response = wrapped_response
        end

        def has_status_code?
          !!status_code
        end

        # most HTTP client libraries report the HTTP status code as an integer, so
        # we expect status_code to be set only if a non-zero value is present
        def status_code
          @status_code ||= get_status_code
        end

        private

        def get_status_code_using(method_name)
          return unless @wrapped_response.respond_to?(method_name)

          code = @wrapped_response.send(method_name).to_i
          code == 0 ? nil : code
        end

        # Override this method to memoize a non-zero Integer representation
        # of HTTP status code from the response object
        def get_status_code
          get_status_code_using(:code)
        end
      end
    end
  end
end
