# frozen_string_literal: true

{{#generated_src_warning}}
{{generated_src_warning}}
{{/generated_src_warning}}
{{#plugin_requires}}
require '{{.}}'
{{/plugin_requires}}

module {{module_name}}
  # An API client for {{service_name}}.  To construct a client, you need to configure a `:region` and `:credentials`.
  #
  #     client = {{module_name}}::Client.new(
  #       region: region_name,
  #       credentials: credentials,
  #       # ...
  #     )
  #
  # For details on configuring region and credentials see
  # the [developer guide](/sdk-for-ruby/v3/developer-guide/setup-config.html).
  #
  # See {#initialize} for a full list of supported configuration options.
  class Client < Seahorse::Client::Base

    include Aws::ClientStubs

    @identifier = :{{service_identifier}}

    set_api(ClientApi::API)

    {{#plugin_class_names}}
    add_plugin({{{.}}})
    {{/plugin_class_names}}

    {{#client_constructor}}
    # @overload initialize(options)
    #   @param [Hash] options
    #
    #   @option options [Array<Seahorse::Client::Plugin>] :plugins ([]])
    #     A list of plugins to apply to the client. Each plugin is either a
    #     class name or an instance of a plugin class.
    #
    {{>documentation}}
    {{/client_constructor}}
    def initialize(*args)
      super
    end

    # @!group API Operations
    {{#operations}}

    {{#documentation}}
    {{>documentation}}
    {{/documentation}}
    # @overload {{name}}(params = {})
    # @param [Hash] params ({})
    def {{name}}(params = {}, options = {}{{{block_option}}})
      {{#eventstream_output}}
      params = params.dup
      event_stream_handler = case handler = params.delete(:event_stream_handler)
        when EventStreams::{{output_eventstream_member}} then handler
        when Proc then EventStreams::{{output_eventstream_member}}.new.tap(&handler)
        when nil then EventStreams::{{output_eventstream_member}}.new
        else
          msg = "expected :event_stream_handler to be a block or "\
                "instance of {{module_name}}::EventStreams::{{output_eventstream_member}}"\
                ", got `#{handler.inspect}` instead"
          raise ArgumentError, msg
        end

      yield(event_stream_handler) if block_given?

      {{/eventstream_output}}
      req = build_request(:{{name}}, params)
      {{#eventstream_output}}

      req.context[:event_stream_handler] = event_stream_handler
      req.handlers.add(Aws::Binary::DecodeHandler, priority: 95)

      {{/eventstream_output}}
      req.send_request(options{{{block_option}}})
    end
    {{/operations}}

    # @!endgroup

    # @param params ({})
    # @api private
    def build_request(operation_name, params = {})
      handlers = @handlers.for(operation_name)
      {{#authorizer?}}
      authorizer = nil
      if config.api.operation(operation_name).authorizer
        authorizer_name = config.api.operation(operation_name).authorizer
        config.api.authorizers.each do |_, auth|
          authorizer = auth if auth.name == authorizer_name
        end
      end
      {{/authorizer?}}
      tracer = config.telemetry_provider.tracer_provider.tracer(
        Aws::Telemetry.module_to_tracer_name('{{module_name}}')
      )
      context = Seahorse::Client::RequestContext.new(
        operation_name: operation_name,
        operation: config.api.operation(operation_name),{{#authorizer?}}
        authorizer: authorizer,{{/authorizer?}}
        client: self,
        params: params,
        config: config,
        tracer: tracer
      )
      context[:gem_name] = '{{gem_name}}'
      context[:gem_version] = '{{gem_version}}'
      Seahorse::Client::Request.new(handlers, context)
    end
    {{#waiters?}}

    # Polls an API operation until a resource enters a desired state.
    #
    # ## Basic Usage
    #
    # A waiter will call an API operation until:
    #
    # * It is successful
    # * It enters a terminal state
    # * It makes the maximum number of attempts
    #
    # In between attempts, the waiter will sleep.
    #
    #     # polls in a loop, sleeping between attempts
    #     client.wait_until(waiter_name, params)
    #
    # ## Configuration
    #
    # You can configure the maximum number of polling attempts, and the
    # delay (in seconds) between each polling attempt. You can pass
    # configuration as the final arguments hash.
    #
    #     # poll for ~25 seconds
    #     client.wait_until(waiter_name, params, {
    #       max_attempts: 5,
    #       delay: 5,
    #     })
    #
    # ## Callbacks
    #
    # You can be notified before each polling attempt and before each
    # delay. If you throw `:success` or `:failure` from these callbacks,
    # it will terminate the waiter.
    #
    #     started_at = Time.now
    #     client.wait_until(waiter_name, params, {
    #
    #       # disable max attempts
    #       max_attempts: nil,
    #
    #       # poll for 1 hour, instead of a number of attempts
    #       before_wait: -> (attempts, response) do
    #         throw :failure if Time.now - started_at > 3600
    #       end
    #     })
    #
    # ## Handling Errors
    #
    # When a waiter is unsuccessful, it will raise an error.
    # All of the failure errors extend from
    # {Aws::Waiters::Errors::WaiterFailed}.
    #
    #     begin
    #       client.wait_until(...)
    #     rescue Aws::Waiters::Errors::WaiterFailed
    #       # resource did not enter the desired state in time
    #     end
    #
    # ## Valid Waiters
    #
    # The following table lists the valid waiter names, the operations they call,
    # and the default `:delay` and `:max_attempts` values.
    #
    {{#waiters_markdown_table}}
    {{#lines}}
    # {{{.}}}{{/lines}}
    {{/waiters_markdown_table}}
    #
    # @raise [Errors::FailureStateError] Raised when the waiter terminates
    #   because the waiter has entered a state that it will not transition
    #   out of, preventing success.
    #
    # @raise [Errors::TooManyAttemptsError] Raised when the configured
    #   maximum number of attempts have been made, and the waiter is not
    #   yet successful.
    #
    # @raise [Errors::UnexpectedError] Raised when an error is encounted
    #   while polling for a resource that is not expected.
    #
    # @raise [Errors::NoSuchWaiterError] Raised when you request to wait
    #   for an unknown state.
    #
    # @return [Boolean] Returns `true` if the waiter was successful.
    # @param [Symbol] waiter_name
    # @param [Hash] params ({})
    # @param [Hash] options ({})
    # @option options [Integer] :max_attempts
    # @option options [Integer] :delay
    # @option options [Proc] :before_attempt
    # @option options [Proc] :before_wait
    def wait_until(waiter_name, params = {}, options = {})
      w = waiter(waiter_name, options)
      yield(w.waiter) if block_given? # deprecated
      w.wait(params)
    end

    # @api private
    # @deprecated
    def waiter_names
      waiters.keys
    end

    private

    # @param [Symbol] waiter_name
    # @param [Hash] options ({})
    def waiter(waiter_name, options = {})
      waiter_class = waiters[waiter_name]
      if waiter_class
        waiter_class.new(options.merge(client: self))
      else
        raise Aws::Waiters::Errors::NoSuchWaiterError.new(waiter_name, waiters.keys)
      end
    end

    def waiters
      {
        {{#waiters}}
        {{name}}: Waiters::{{class_name}}{{^last}},{{/last}}
        {{/waiters}}
      }
    end
    {{/waiters?}}
    {{^waiters?}}

    # @api private
    # @deprecated
    def waiter_names
      []
    end
    {{/waiters?}}

    class << self

      # @api private
      attr_reader :identifier

      # @api private
      def errors_module
        Errors
      end

    end
  end
end
