class Rack::Attack

  # The following is a useful resource.
  # https://www.driftingruby.com/episodes/rails-api-throttling-with-rack-attack
  #
  ### Configure Cache ###

  # If you don't want to use Rails.cache (Rack::Attack's default), then
  # configure it here.
  #
  # Note: The store is only used for throttling (not blocklisting and
  # safelisting). It must implement .increment and .write like
  # ActiveSupport::Cache::Store

  # Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new

  ### Throttle Spammy Clients ###

  # If we fail to unmask the remote IP for a request, the
  # frontends will pass the internal network (10.0.0.0/8) to the
  # unicorns. We need to ensure that we don't block these requests.

  ArchiveConfig.RATE_LIMIT_SAFELIST.each do |ip|
    Rack::Attack.safelist_ip(ip)
  end

  # If any single client IP is making tons of requests, then they're
  # probably malicious or a poorly-configured scraper. Either way, they
  # don't deserve to hog all of the app server's CPU. Cut them off!
  #
  # Note: If you're serving assets through rack, those requests may be
  # counted by rack-attack and this throttle may be activated too
  # quickly. If so, enable the condition to exclude them from tracking.
  #

  # This stanza allows us to limit by user which backend is selected by nginx.
  
  ArchiveConfig.RATE_LIMIT_PER_NGINX_UPSTREAM_USER.each do |k, v| 
    throttle("req/#{k}/user", limit: v["limit"], period: v["period"]) do |req|
      req.env["HTTP_X_AO3_SESSION_USER"] if req.env["HTTP_X_UNICORNS"] == k && req.env["HTTP_X_AO3_SESSION_USER"].present?
    end
  end

  # This stanza allows us to limit by ip which backend is selected by nginx.

  ArchiveConfig.RATE_LIMIT_PER_NGINX_UPSTREAM.each do |k, v|
    throttle("req/#{k}/ip", limit: v["limit"], period: v["period"]) do |req|
      req.ip if req.env["HTTP_X_UNICORNS"] == k
    end
  end

  # Throttle all requests by IP (60rpm)
  #
  # Key: "rack::attack:#{Time.now.to_i/:period}:req/ip:#{req.ip}"
  limit = ArchiveConfig.RATE_LIMIT_NUMBER
  period = ArchiveConfig.RATE_LIMIT_PERIOD
  throttle('req/ip', limit: limit, period: period) do |req|
    req.ip
  end

  ### Prevent Brute-Force Login Attacks ###

  # The most common brute-force login attack is a brute-force password
  # attack where an attacker simply tries a large number of emails and
  # passwords to see if any credentials match.
  #
  # Another common method of attack is to use a swarm of computers with
  # different IPs to try brute-forcing a password for a specific account.

  login_limit = ArchiveConfig.RATE_LIMIT_LOGIN_ATTEMPTS
  login_period = ArchiveConfig.RATE_LIMIT_LOGIN_PERIOD

  # Throttle POST requests to /users/login by IP address
  #
  # Key: "rack::attack:#{Time.now.to_i/:period}:logins/ip:#{req.ip}"
  throttle("logins/ip", limit: login_limit, period: login_period) do |req|
    req.ip if req.path == "/users/login" && req.post?
  end

  # Throttle POST requests to /users/login by login param (username or email)
  #
  # Key: "rack::attack:#{Time.now.to_i/:period}:logins/email:#{login}"
  #
  # Note: This creates a problem where a malicious user could intentionally
  # throttle logins for another user and force their login requests to be
  # denied, but that's not very common and shouldn't happen to you. (Knock
  # on wood!)
  throttle("logins/email", limit: login_limit, period: login_period) do |req|
    req.params.dig("user", "login").presence if req.path == "/users/login" && req.post?
  end
  
  ### Add Rate Limits for Admin Login ###
  admin_login_limit = ArchiveConfig.RATE_LIMIT_ADMIN_LOGIN_ATTEMPTS
  admin_login_period = ArchiveConfig.RATE_LIMIT_ADMIN_LOGIN_PERIOD
  
  # Throttle POST requests to /admin/login by IP address
  #
  # Key: "rack::attack:#{Time.now.to_i/:period}:admin_logins/ip:#{req.ip}"
  throttle("admin_logins/ip", limit: admin_login_limit, period: admin_login_period) do |req|
    req.ip if req.path == "/admin/login" && req.post?
  end

  # Throttle POST requests to /admin/login by login param (user name or email)
  #
  # Key: "rack::attack:#{Time.now.to_i/:period}:admin_logins/email:#{login}"
  throttle("admin_logins/email", limit: admin_login_limit, period: admin_login_period) do |req|
    req.params.dig("admin", "login").presence if req.path == "/admin/login" && req.post?
  end
  
  # Add Retry-After response header to let polite clients know
  # how many seconds they should wait before trying again
  Rack::Attack.throttled_response_retry_after_header = true

  ### Custom Throttle Response ###

  # By default, Rack::Attack returns an HTTP 429 for throttled responses,
  # which is just fine.
  #
  # If you want to return 503 so that the attacker might be fooled into
  # believing that they've successfully broken your app (or you just want to
  # customize the response), then uncomment these lines.
  # self.throttled_response = lambda do |env|
  #  [ 503,  # status
  #    {},   # headers
  #    ['']] # body
  # end
end
