# frozen_string_literal: true

RSpec.describe "RequestTracker in multisite", type: :multisite do
  before do
    global_setting :skip_per_ip_rate_limit_trust_level, 2

    RateLimiter.enable

    test_multisite_connection("default") do
      RateLimiter.clear_all!
    end
    test_multisite_connection("second") do
      RateLimiter.clear_all!
    end
    RateLimiter.clear_all_global!
  end

  def call(env, &block)
    Middleware::RequestTracker.new(block).call(env)
  end

  def create_env(opts)
    create_request_env.merge(opts)
  end

  shared_examples "ip rate limiters behavior" do |error_code, app_callback|
    it "applies rate limits on an IP address across all sites" do
      called = { default: 0, second: 0 }
      test_multisite_connection("default") do
        env = create_env("REMOTE_ADDR" => "123.10.71.4")
        status, = call(env) do
          called[:default] += 1
          app_callback&.call(env)
          [200, {}, ["OK"]]
        end
        expect(status).to eq(200)

        env = create_env("REMOTE_ADDR" => "123.10.71.4")
        status, headers = call(env) do
          called[:default] += 1
          app_callback&.call(env)
          [200, {}, ["OK"]]
        end
        expect(status).to eq(429)
        expect(headers["Discourse-Rate-Limit-Error-Code"]).to eq(error_code)
        expect(called[:default]).to eq(1)
      end

      test_multisite_connection("second") do
        env = create_env("REMOTE_ADDR" => "123.10.71.4")
        status, headers = call(env) do
          called[:second] += 1
          app_callback&.call(env)
          [200, {}, ["OK"]]
        end
        expect(status).to eq(429)
        expect(headers["Discourse-Rate-Limit-Error-Code"]).to eq(error_code)
        expect(called[:second]).to eq(0)
      end
    end
  end

  shared_examples "user id rate limiters behavior" do |error_code, app_callback|
    it "does not leak rate limits for a user id to other sites" do
      cookie = create_auth_cookie(
        token: SecureRandom.hex,
        user_id: 1,
        trust_level: 2
      )
      called = { default: 0, second: 0 }
      test_multisite_connection("default") do
        env = create_env("REMOTE_ADDR" => "123.10.71.4", "HTTP_COOKIE" => "_t=#{cookie}")
        status, = call(env) do
          called[:default] += 1
          app_callback&.call(env)
          [200, {}, ["OK"]]
        end
        expect(status).to eq(200)

        env = create_env("REMOTE_ADDR" => "123.10.71.4", "HTTP_COOKIE" => "_t=#{cookie}")
        status, headers, = call(env) do
          called[:default] += 1
          app_callback&.call(env)
          [200, {}, ["OK"]]
        end
        expect(status).to eq(429)
        expect(headers["Discourse-Rate-Limit-Error-Code"]).to eq(error_code)
        expect(called[:default]).to eq(1)
      end

      test_multisite_connection("second") do
        env = create_env("REMOTE_ADDR" => "123.10.71.4", "HTTP_COOKIE" => "_t=#{cookie}")
        status, = call(env) do
          called[:second] += 1
          app_callback&.call(env)
          [200, {}, ["OK"]]
        end
        expect(status).to eq(200)

        env = create_env("REMOTE_ADDR" => "123.10.71.4", "HTTP_COOKIE" => "_t=#{cookie}")
        status, headers, = call(env) do
          called[:second] += 1
          app_callback&.call(env)
          [200, {}, ["OK"]]
        end
        expect(status).to eq(429)
        expect(headers["Discourse-Rate-Limit-Error-Code"]).to eq(error_code)
        expect(called[:second]).to eq(1)
      end
    end
  end

  context "with a 10 seconds limiter" do
    before do
      global_setting :max_reqs_per_ip_per_10_seconds, 1
    end

    include_examples "ip rate limiters behavior", "ip_10_secs_limit"
    include_examples "user id rate limiters behavior", "id_10_secs_limit"
  end

  context "with a 60 seconds limiter" do
    before do
      global_setting :max_reqs_per_ip_per_minute, 1
    end

    include_examples "ip rate limiters behavior", "ip_60_secs_limit"
    include_examples "user id rate limiters behavior", "id_60_secs_limit"
  end

  context "with assets 10 seconds limiter" do
    before do
      global_setting :max_asset_reqs_per_ip_per_10_seconds, 1
    end

    app_callback = ->(env) { env["DISCOURSE_IS_ASSET_PATH"] = true }
    include_examples "ip rate limiters behavior", "ip_assets_10_secs_limit", app_callback
    include_examples "user id rate limiters behavior", "id_assets_10_secs_limit", app_callback
  end
end