class LHC::Throttle

def after_response

def after_response
  options = response.request.options.dig(:throttle)
  return unless throttle?(options)
  self.class.track ||= {}
  self.class.track[options.dig(:provider)] = {
    limit: limit(options: options[:limit], response: response),
    remaining: remaining(options: options[:remaining], response: response),
    expires: expires(options: options[:expires], response: response)
  }
end

def before_request

def before_request
  options = request.options.dig(:throttle)
  return unless options
  break_options = options.dig(:break)
  return unless break_options
  break_when_quota_reached! if break_options.match?('%')
end

def break_when_quota_reached!

def break_when_quota_reached!
  options = request.options.dig(:throttle)
  track = (self.class.track || {}).dig(options[:provider])
  return if track.blank? || track[:remaining].blank? || track[:limit].blank? || track[:expires].blank?
  return if Time.zone.now > track[:expires]
  # avoid floats by multiplying with 100
  remaining = track[:remaining] * 100
  limit = track[:limit]
  quota = 100 - options[:break].to_i
  raise(OutOfQuota, "Reached predefined quota for #{options[:provider]}") if remaining < quota * limit
end

def convert_expires(value)

def convert_expires(value)
  return if value.blank?
  return value.call(response) if value.is_a?(Proc)
  return Time.parse(value) if value.match?(/GMT/)
  Time.zone.at(value.to_i).to_datetime
end

def expires(options:, response:)

def expires(options:, response:)
  @expires ||= convert_expires(read_expire_option(options, response))
end

def limit(options:, response:)

def limit(options:, response:)
  @limit ||=
    if options.is_a?(Proc)
      options.call(response)
    elsif options.is_a?(Integer)
      options
    elsif options.is_a?(Hash) && options[:header]
      response.headers[options[:header]]&.to_i
    end
end

def read_expire_option(options, response)

def read_expire_option(options, response)
  (options.is_a?(Hash) && options[:header]) ? response.headers[options[:header]] : options
end

def remaining(options:, response:)

def remaining(options:, response:)
  @remaining ||=
    begin
      if options.is_a?(Proc)
        options.call(response)
      elsif options.is_a?(Hash) && options[:header]
        response.headers[options[:header]]&.to_i
      end
    end
end

def throttle?(options)

def throttle?(options)
  [options&.dig(:track), response.headers].none?(&:blank?)
end