-- BloxMind Runtime SDK v1.0
-- Server-side only. Put this ModuleScript in ServerScriptService or ServerStorage.
-- Never place your runtime key in a LocalScript, ReplicatedStorage, or public client code.

local HttpService = game:GetService("HttpService")
local RunService = game:GetService("RunService")

assert(RunService:IsServer(), "BloxMind Runtime must only run on the server.")

local Runtime = {}

Runtime.Config = {
    ApiBase = "https://bloxmind.onrender.com",
    RuntimeKey = "PASTE_BLOXMIND_RUNTIME_KEY_HERE",
    RequestTimeoutNote = "HttpService requests use Roblox-managed network timeouts.",
}

local function assertConfigured()
    if type(Runtime.Config.RuntimeKey) ~= "string" or not string.match(Runtime.Config.RuntimeKey, "^bmrt_") then
        error("BloxMind Runtime is not configured. Generate a Runtime key in BloxMind Studio Bridge and paste it into Runtime.Config.RuntimeKey.")
    end
end

local function request(method, path, body)
    assertConfigured()
    local request = {
        Url = Runtime.Config.ApiBase .. path,
        Method = method,
        Headers = {
            ["Content-Type"] = "application/json",
            ["X-BloxMind-Runtime-Key"] = Runtime.Config.RuntimeKey,
        },
    }
    if body ~= nil then
        request.Body = HttpService:JSONEncode(body)
    end

    local ok, response = pcall(function()
        return HttpService:RequestAsync(request)
    end)
    if not ok then
        return nil, "BloxMind Runtime request failed: " .. tostring(response)
    end
    if not response.Success then
        local detail = response.Body
        pcall(function()
            local decoded = HttpService:JSONDecode(response.Body)
            if type(decoded) == "table" and decoded.detail then
                detail = decoded.detail
            end
        end)
        return nil, string.format("BloxMind Runtime HTTP %s: %s", tostring(response.StatusCode), tostring(detail))
    end

    if response.Body == nil or response.Body == "" then
        return {}, nil
    end
    local decodeOk, decoded = pcall(function()
        return HttpService:JSONDecode(response.Body)
    end)
    if not decodeOk then
        return nil, "BloxMind Runtime returned an unreadable response."
    end
    return decoded, nil
end

local function playerId(playerOrUserId)
    if typeof(playerOrUserId) == "Instance" and playerOrUserId:IsA("Player") then
        return tostring(playerOrUserId.UserId)
    end
    return tostring(playerOrUserId)
end

-- Send only events your game intentionally chooses to share.
-- Example: Runtime.SendEvent(player, "boss_defeated", { boss = "VoidKnight" })
function Runtime.SendEvent(playerOrUserId, eventName, payload)
    assert(type(eventName) == "string" and #eventName > 0, "eventName is required")
    payload = type(payload) == "table" and payload or {}
    return request("POST", "/api/runtime/events", {
        event_name = string.lower(eventName),
        roblox_user_id = playerId(playerOrUserId),
        place_id = tostring(game.PlaceId),
        universe_id = tostring(game.GameId),
        payload = payload,
    })
end

-- Get the active BloxMind Goal for a linked player in this connected experience.
function Runtime.GetCurrentGoal(playerOrUserId)
    local id = HttpService:UrlEncode(playerId(playerOrUserId))
    return request("GET", "/api/runtime/player-goal?roblox_user_id=" .. id)
end

-- Mark a BloxMind Goal complete from trusted server-side game logic.
-- Only a goal belonging to the linked user and this Creator Project can be verified.
function Runtime.CompleteGoal(playerOrUserId, goalId, extraPayload)
    assert(type(goalId) == "string" and #goalId > 0, "goalId is required")
    local payload = type(extraPayload) == "table" and extraPayload or {}
    payload.goal_id = goalId
    return Runtime.SendEvent(playerOrUserId, "goal_completed", payload)
end

return Runtime
