-- Constants
-- configured by env
local AUTH_SERVICE_URL = os.getenv("AUTH_SERVICE_URL") or "http://regionalservice.observability.svc.cluster.local:8081"
local CACHE_EXPIRE_TIME = tonumber(os.getenv("CACHE_EXPIRE_TIME")) or 3600
local ERROR_CACHE_EXPIRE_TIME = tonumber(os.getenv("ERROR_CACHE_EXPIRE_TIME")) or 60
local AUTH_PATH = "/api/v1/auth/?path="
local AUTH_NO_PROJECTID = "&ignoreProject=true"
local AUTH_O11Y_ID = "&useO11yID=true"
local VMSELECT_API_PREFIX = "/select/"

-- Dependencies
local ngx = ngx
local o11y_route_cache = ngx.shared.o11y_route_data
local json = require("cjson")
local http = require("resty.http")

-- Fetch data from authentication service
-- @param path (string) The path to authenticate
-- @param path_type (string) The type of path (original, ignoreProject, or useO11yID)
-- @return table {vmTenant = string, code = number}
local function fetch_origin(path, path_type)
    if type(path) ~= "string" then
        return { code = ngx.HTTP_BAD_REQUEST }
    end

    local httpc = http.new()
    local url = AUTH_SERVICE_URL .. AUTH_PATH .. ngx.escape_uri(path)

    -- Get header values
    local headers = ngx.req.get_headers()
    local req_headers = {
      ["Content-Type"] = "application/json"
    }

    if headers["X-Biz-Type"] then
      req_headers["X-Biz-Type"] = headers["X-Biz-Type"]
    end
    if headers["X-Org-Id"] then
      req_headers["X-Org-Id"] = headers["X-Org-Id"]
    end
    if headers["X-Application-Id"] then
      req_headers["X-Application-Id"] = headers["X-Application-Id"]
    end

    -- Append additional parameters based on path_type
    if path_type == "ignoreProject" then
        url = url .. AUTH_NO_PROJECTID
    elseif path_type == "useO11yID" then
        url = url .. AUTH_O11Y_ID
    end

    local res, err = httpc:request_uri(url, {
        method = "GET",
        headers = req_headers
    })

    if not res then
        ngx.log(ngx.ERR, "Failed to fetch data from origin: ", err)
        return { code = ngx.HTTP_INTERNAL_SERVER_ERROR }
    end

    if math.floor(res.status / 100) ~= 2 then
        ngx.log(ngx.ERR, "Failed to fetch data from origin. Status: ", res.status)
        return { code = res.status }
    end

    local ok, response = pcall(json.decode, res.body)
    if not ok then
        ngx.log(ngx.ERR, "Failed to decode JSON response: ", response)
        return { code = ngx.HTTP_INTERNAL_SERVER_ERROR }
    end

    return {
        vmTenant = response.data.vmAccountID .. ":" .. response.data.vmProjectID,
        code = res.status
    }
end

-- Rewrite URL based on the given path type
-- @param path_type (string) The type of path (original, ignoreProject, or useO11yID)
local function rewrite_url(path_type)
    path_type = path_type or "original" -- Default to "original" if not provided
    local path = ngx.var.uri
    local key, cache_path
    -- Cursor to track the correct index api for ngx.var
    local cursor_index
    -- Determine the cache key and path based on path_type
    if path_type == "ignoreProject" then
        -- Matching /external/metrics/(.*-devtier)/tenant/(.*)/application/(.*)/(api/v1/.*)
        key = table.concat({ ngx.var[1], ngx.var[2], ngx.var[3] })
        cursor_index = 4
    elseif path_type == "useO11yID" then
        -- Matching /internal/v2/metrics/(.*)/(api/v1/.*)
        key = ngx.var[1]
        cursor_index = 2
    else
        -- Default: original
        key = table.concat({ ngx.var[1], ngx.var[2], ngx.var[3], ngx.var[4] })
        cursor_index = 5
    end

    local val = o11y_route_cache:get(key)
    local data

    if val then
        -- Cache hit
        local ok, decoded = pcall(json.decode, val)
        if not ok then
            ngx.log(ngx.ERR, "Failed to decode cached data: ", decoded)
            data = fetch_origin(path, path_type)
        else
            data = decoded
        end
        if math.floor(data.code / 100) ~= 2 then
            ngx.log(ngx.ERR, "Cached response code for request " .. path .. " is not 2xx: ", data.code)
            ngx.status = data.code
            ngx.exit(data.code)
            return
        end
    else
        -- Cache miss
        data = fetch_origin(path, path_type)
        local cache_expire_time = (math.floor(data.code / 100) == 2) and CACHE_EXPIRE_TIME or ERROR_CACHE_EXPIRE_TIME
        o11y_route_cache:set(key, json.encode(data), cache_expire_time)
        if math.floor(data.code / 100) ~= 2 then
            ngx.status = data.code
            ngx.exit(data.code)
            return
        end
    end
    -- TODO: differentiate the prefix between different services
    local new_uri = VMSELECT_API_PREFIX .. data.vmTenant .. "/prometheus/" .. ngx.var[cursor_index]
    ngx.log(ngx.INFO, "Rewriting URL from ", path, " to ", new_uri)
    ngx.req.set_uri(new_uri, false)
end

-- Expose functions
return {
    rewrite_url = rewrite_url
}
