# frozen_string_literal: true

require 'awspec/config'

module Awspec::Helper
  class ClientWrap
    attr_reader :client, :backoff, :iteration, :backoff_limit, :symbol1, :symbol2

    def initialize(real_client = nil)
      raise ArgumentError, 'Client can not be nil' if real_client.nil?

      config = Awspec::Config.instance
      @client        = real_client
      @backoff       = config[:client_backoff]
      @orig_backoff  = @backoff
      @iteration     = config[:client_iteration]
      @orig_iter     = @iteration
      @backoff_limit = config[:client_backoff_limit]
      # build the symbol we'll use to compare to any errors caught in method_missing
      # below.
      @symbol1 = real_client.class.to_s.split('::').shift(2).push('Errors', 'RequestLimitExceeded').join('::').to_sym
      @symbol2 = real_client.class.to_s.split('::').shift(2).push('Errors', 'Throttling').join('::').to_sym
    end

    protected

    # used to capture only the "RequestLimitExceeded" error from an aws
    # client api call.  In the case of matching it we want to try again,
    # backing off successively each time, until the backoff_limit is reached or
    # exceeded, in which case, the error will be re-raised and it should fail
    # as expected.
    def method_missing(m, *args, &block)
      begin
        results = client.send(m, *args, &block)
      rescue Exception => e # rubocop:disable Lint/RescueException
        raise unless (e.class.to_s == symbol1.to_s || e.class.to_s == symbol2.to_s) && backoff < backoff_limit

        @backoff = backoff + (iteration * iteration * 0.5)
        @iteration += 1
        sleep backoff
        results = send(m, *args, &block)
      end

      reset_backoff

      results
    end

    def reset_backoff
      @backoff   = @orig_backoff
      @iteration = @orig_iter
    end
  end
end
