local helpers = require "spec.helpers"
local md5 = require "resty.md5"
local str = require "resty.string"

local REDIS_HOST = helpers.redis_host
local REDIS_PORT = helpers.redis_port
local REDIS_PASSWORD = ""
local REDIS_DATABASE = 1

-- This performs the test up to two times (and no more than two).
-- We are **not** retrying to "give it another shot" in case of a flaky test.
-- The reason why we allow for a single retry in this test suite is because
-- tests are dependent on the value of the current minute. If the minute
-- flips during the test (i.e. going from 03:43:59 to 03:44:00), the result
-- will fail. Since each test takes less than a minute to run, running it
-- a second time right after that failure ensures that another flip will
-- not occur. If the second execution failed as well, this means that there
-- was an actual problem detected by the test.
local function it_with_retry(desc, test)
    return it(desc, function(...)
        if not pcall(test, ...) then
            ngx.sleep(61 - (ngx.now() % 60)) -- Wait for minute to expire
            test(...)
        end
    end)
end

local function flush_redis()
    local redis = require "resty.redis"
    local red = redis:new()
    red:set_timeout(2000)
    local ok, err = red:connect(REDIS_HOST, REDIS_PORT)
    if not ok then
        error("failed to connect to Redis: " .. err)
    end

    if REDIS_PASSWORD and REDIS_PASSWORD ~= "" then
        local ok, err = red:auth(REDIS_PASSWORD)
        if not ok then
            error("failed to connect to Redis: " .. err)
        end
    end

    local ok, err = red:select(REDIS_DATABASE)
    if not ok then
        error("failed to change Redis database: " .. err)
    end

    red:flushall()
    red:close()
end

-- Digest username="apjcejac", realm="tidb.cloud", nonce="4tfOBX/XnfNQ7XAuJ8WTuCEhR2t1SpwW", uri="/status/200", cnonce="YzI2ZTYzNTFiMDk0MzFlMDQxOGNjMDEwMTYyNjRkNmE=", nc=00000001, qop=auth, response="53c2f533bc405ee75dff2c67431b518e", algorithm=MD5

for _, strategy in helpers.each_strategy() do
    describe("Plugin: rate-limit [#" .. strategy .. "]", function()
        local proxy_client
        lazy_setup(function()
            flush_redis()

            local bp = helpers.get_db_utils(strategy)

            local service1 = bp.services:insert{
                protocol = "http",
                host = helpers.mock_upstream_host,
                port = helpers.mock_upstream_port
            }

            local route1 = bp.routes:insert{
                protocols = {"http", "https"},
                hosts = {"test1.com"},
                service = service1
            }

            bp.plugins:insert{
                route = {id = route1.id},
                name = "rate-limit",
                config = {
                    header_name = "test",
                    minute = 6,
                    fault_tolerant = false,
                    redis_host = REDIS_HOST,
                    redis_port = REDIS_PORT,
                    redis_database = REDIS_DATABASE
                }
            }

            assert(helpers.start_kong({
                database = strategy,
                nginx_conf = "spec/fixtures/custom_nginx.template",
                plugins = "rate-limit"
            }))
        end)

        lazy_teardown(function()
            helpers.stop_kong()
        end)

        before_each(function()
            proxy_client = helpers.proxy_client()
        end)

        after_each(function()
            if proxy_client then
                proxy_client:close()
            end
        end)

        describe("Rate Limit", function()
            it_with_retry("exceed requests return 429", function()
                for i = 1, 6 do
                    local res = assert(proxy_client:send({
                        method = "GET",
                        path = "/status/200",
                        headers = {["Host"] = "test1.com", ["test"] = "123"}
                    }))
                    assert.res_status(200, res)

                    helpers.wait_timer("rate-limiting", true, "any-finish")
                end

                for i = 1, 6 do
                    local res = assert(proxy_client:send({
                        method = "GET",
                        path = "/status/200",
                        headers = {["Host"] = "test1.com", ["test"] = "123"}
                    }))
                    assert.res_status(429, res)

                    helpers.wait_timer("rate-limiting", true, "any-finish")
                end
            end)
        end)
    end)
end
