local HttpService = game:GetService("HttpService")

local Util = {}

function Util.DeepCopy(value, seen)
    if type(value) ~= "table" then
        return value
    end

    seen = seen or {}
    if seen[value] then
        error("NexusData does not support cyclic tables")
    end

    seen[value] = true
    local copy = {}
    for key, item in pairs(value) do
        if type(key) == "table" or type(key) == "userdata" or type(key) == "function" then
            error("NexusData only supports serializable table keys")
        end
        copy[key] = Util.DeepCopy(item, seen)
    end
    seen[value] = nil
    return copy
end

function Util.Reconcile(data, template)
    for key, value in pairs(template) do
        if data[key] == nil then
            data[key] = Util.DeepCopy(value)
        elseif type(data[key]) == "table" and type(value) == "table" then
            Util.Reconcile(data[key], value)
        end
    end
    return data
end

function Util.IsValidKey(key)
    return type(key) == "string" or type(key) == "number"
end

function Util.Encode(value)
    local ok, encoded = pcall(function()
        return HttpService:JSONEncode(value)
    end)
    if ok then
        return encoded
    end
    return nil, encoded
end

function Util.Backoff(attempt, baseDelay, maxDelay)
    return math.min(baseDelay * (2 ^ (attempt - 1)), maxDelay) + math.random() * 0.25
end

return Util
