local helpers = require "spec.helpers"
local cjson = require "cjson"

-- Dynamic port for the mock auth service
local mock_auth_port = helpers.get_available_port()

local function get_mock_auth_service(port)
    return [[
        server {
            listen ]] .. port .. [[;
            location /auth {
                content_by_lua_block {
                    local cjson = require "cjson"
                    ngx.req.read_body()
                    local data = ngx.req.get_body_data()
                    local body = data and cjson.decode(data) or {}

                    if body.sid == "valid_sid" then
                        ngx.status = 200
                        ngx.header["Content-Type"] = "application/json"
                        ngx.say(cjson.encode({
                            ok = true,
                            auth0_token = "mock_auth0_token",
                            user_meta = { id = "user123", role = "admin" }
                        }))
                    else
                        ngx.status = 200
                        ngx.header["Content-Type"] = "application/json"
                        ngx.say(cjson.encode({
                            ok = false,
                            message = "Unauthorized"
                        }))
                    end
                }
            }
        }
    ]]
end

for _, strategy in helpers.each_strategy() do
    describe("Plugin: cookie-auth [#" .. strategy .. "]", function()
        local proxy_client
        local auth_service_fixture = get_mock_auth_service(mock_auth_port)

        lazy_setup(function()
            local bp = helpers.get_db_utils(strategy, { "routes", "services", "plugins" })

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

            local route = bp.routes:insert({
                protocols = { "http" },
                hosts = { "cookie-auth.test" },
                service = service
            })

            bp.plugins:insert({
                route = { id = route.id },
                name = "cookie-auth",
                config = {
                    auth_url = "http://127.0.0.1:" .. mock_auth_port .. "/auth",
                    env_type = "dev",
                    timeout = 2000,
                    whitelist = {
                        { path = "/healthz" }
                    },
                    static_suffixes = { "%.css$" }
                }
            })

            -- Start Kong with the custom Nginx fixture for the Auth Service
            assert(helpers.start_kong({
                database = strategy,
                nginx_conf = "spec/fixtures/custom_nginx.template",
                plugins = "cookie-auth",
                nginx_http_include = auth_service_fixture -- Inject our mock server
            }))
        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("Access Control", function()
            it("should deny request without cookie", function()
                local res = assert(proxy_client:send({
                    method = "GET",
                    path = "/status/200",
                    headers = {
                        ["Host"] = "cookie-auth.test"
                    }
                }))
                assert.res_status(401, res)
            end)

            it("should deny request with invalid cookie (Mock Auth rejects)", function()
                local res = assert(proxy_client:send({
                    method = "GET",
                    path = "/status/200",
                    headers = {
                        ["Host"] = "cookie-auth.test",
                        ["Cookie"] = "account_sid_dev=invalid_sid"
                    }
                }))
                assert.res_status(401, res)
                local body = cjson.decode(assert.res_body(res))
                assert.equal("Invalid session", body.message)
            end)

            it("should deny request with cookie for wrong env (Host logic check)", function()
                -- Configured env_type is "dev", so it expects "account_sid_dev"
                -- If we send "account_sid" (prod), it should fail to find the SID.
                local res = assert(proxy_client:send({
                    method = "GET",
                    path = "/status/200",
                    headers = {
                        ["Host"] = "cookie-auth.test",
                        ["Cookie"] = "account_sid=valid_sid"
                    }
                }))
                assert.res_status(401, res)
                -- Log would say "no account_sid_dev in cookie"
            end)

            it("should allow request with valid cookie", function()
                local res = assert(proxy_client:send({
                    method = "GET",
                    path = "/status/200",
                    headers = {
                        ["Host"] = "cookie-auth.test",
                        ["Cookie"] = "account_sid_dev=valid_sid"
                    }
                }))
                assert.res_status(200, res)

                -- Verify upstream received the headers injected by plugin
                local upstream_headers = assert.mock_upstream_request_headers()
                assert.equal("Bearer mock_auth0_token", upstream_headers["authorization"])
                assert.equal("cookie", upstream_headers["x-auth-method"])
            end)

            it("should bypass auth for whitelisted path", function()
                local res = assert(proxy_client:send({
                    method = "GET",
                    path = "/healthz",
                    headers = {
                        ["Host"] = "cookie-auth.test"
                    }
                }))
                assert.res_status(200, res)
                -- Should NOT have the auth headers
                -- (Note: mock_upstream_request_headers only works if the request actually hit the upstream)
                -- If status is 200, it hit the upstream.
            end)

            it("should bypass auth for static resources", function()
                local res = assert(proxy_client:send({
                    method = "GET",
                    path = "/main.css",
                    headers = {
                        ["Host"] = "cookie-auth.test"
                    }
                }))
                assert.res_status(200, res)
            end)
        end)
    end)
end
