-- Vine Keysystem — minimalist LuaU loader (same black/green style)
-- Put your Project ID + public API key below. The game secret (x-api-key)
-- is a *public project key*, safe to ship in scripts (per-project).
-- Never ship Supabase service_role keys in Roblox.
--
-- Keys are signed and self-contained: expiry + optional HWID lock travel
-- with the key. Nothing is stored server-side.
-- Players get keys at YOUR key page (with ad checkpoints):
--   https://vinekey.space/key.html?project=YOUR_PROJECT_ID

local Vine = {}
Vine.Config = {
    BaseUrl = "https://vinekey.space/v1", -- your deployment (no trailing slash)
    ProjectId = "YOUR_PROJECT_ID",
    ApiKey = "YOUR_PROJECT_API_KEY", -- vine_... (project key, revocable)
    CooldownSeconds = 5, -- client-side delay between Verify attempts
    RateLimitedCooldown = 30, -- delay after the server says rate_limited
}

local HttpService = game:GetService("HttpService")
local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")

local function getHwid()
    -- Replace with your executor/HWID method. Must match ^[A-Za-z0-9:_.-]{6,128}$
    local ok, id = pcall(function() return gethwid and gethwid() end)
    if ok and type(id) == "string" and #id >= 6 then return id end
    return tostring(Players.LocalPlayer and Players.LocalPlayer.UserId or "unknown-hwid")
end

function Vine.Verify(key)
    assert(type(key) == "string" and #key > 10, "Vine: key required")
    local url = Vine.Config.BaseUrl .. "/verify?project=" .. HttpService:UrlEncode(Vine.Config.ProjectId)
    local ok, res = pcall(function()
        return request({
            Url = url,
            Method = "GET",
            Headers = {
                ["x-api-key"] = Vine.Config.ApiKey,
                ["x-project-id"] = Vine.Config.ProjectId,
                ["x-hwid"] = getHwid(),
                ["x-key"] = key,
            },
        })
    end)
    if not ok or not res then return false, "request_failed" end
    if res.StatusCode == 429 then return false, "rate_limited" end
    local data
    local ok2, err = pcall(function()
        data = HttpService:JSONDecode(res.Body)
    end)
    if not ok2 or not data then return false, "bad_response" end
    if data.valid == true then return true, data end
    return false, data.reason or data.error or "invalid_key"
end

-- Key-page link carrying this device's HWID (?hwid= is hidden on the
-- page — the minted key is silently device-locked).
function Vine.KeyPageLink()
    local base = Vine.Config.BaseUrl:gsub("/v1/*$", "")
    return base .. "/key.html?project=" .. HttpService:UrlEncode(Vine.Config.ProjectId)
        .. "&hwid=" .. HttpService:UrlEncode(getHwid())
end

-- Minimalist key UI (black + vine green, matches website). Draggable top
-- bar, close button, Verify + Get Key buttons, per-attempt cooldown.
function Vine.Prompt(callback)
    local cooldownUntil = 0
    local gui = Instance.new("ScreenGui")
    gui.Name = "VineKey"
    gui.ResetOnSpawn = false
    gui.IgnoreGuiInset = true
    local frame = Instance.new("Frame")
    frame.Size = UDim2.new(0, 340, 0, 330)
    frame.Position = UDim2.new(0.5, -170, 0.5, -165)
    frame.BackgroundColor3 = Color3.fromRGB(13, 15, 13)
    frame.BorderSizePixel = 0
    frame.Parent = gui
    local corner = Instance.new("UICorner") corner.CornerRadius = UDim.new(0, 14) corner.Parent = frame
    local stroke = Instance.new("UIStroke") stroke.Color = Color3.fromRGB(28, 33, 28) stroke.Thickness = 1 stroke.Parent = frame
    local vine = Instance.new("Frame")
    vine.Size = UDim2.new(1, 0, 0, 6)
    vine.BackgroundColor3 = Color3.fromRGB(22, 163, 74)
    vine.BorderSizePixel = 0
    vine.Parent = frame
    local stripCorner = Instance.new("UICorner") stripCorner.CornerRadius = UDim.new(0, 14) stripCorner.Parent = vine
    local bar = Instance.new("Frame")
    bar.Size = UDim2.new(1, 0, 0, 52)
    bar.Position = UDim2.new(0, 0, 0, 6)
    bar.BackgroundTransparency = 1
    bar.Parent = frame
    local title = Instance.new("TextLabel")
    title.Size = UDim2.new(1, -60, 1, 0) title.Position = UDim2.new(0, 20, 0, 0)
    title.BackgroundTransparency = 1
    title.Text = "Vine" title.Font = Enum.Font.GothamBold title.TextSize = 26
    title.TextColor3 = Color3.fromRGB(34, 197, 94)
    title.TextXAlignment = Enum.TextXAlignment.Left
    title.Parent = bar
    local closeBtn = Instance.new("TextButton")
    closeBtn.Size = UDim2.new(0, 28, 0, 28) closeBtn.Position = UDim2.new(1, -40, 0, 12)
    closeBtn.Text = "x" closeBtn.Font = Enum.Font.GothamBold closeBtn.TextSize = 16
    closeBtn.BackgroundColor3 = Color3.fromRGB(24, 28, 24)
    closeBtn.TextColor3 = Color3.fromRGB(154, 154, 148)
    closeBtn.Parent = bar
    local closeCorner = Instance.new("UICorner") closeCorner.CornerRadius = UDim.new(0, 8) closeCorner.Parent = closeBtn
    closeBtn.MouseButton1Click:Connect(function() gui:Destroy() end)
    -- Drag (mouse + touch): press the top bar to move the window.
    local dragging = false
    local dragStart, startPos
    bar.InputBegan:Connect(function(input)
        if input.UserInputType == Enum.UserInputType.MouseButton1
            or input.UserInputType == Enum.UserInputType.Touch then
            dragging = true
            dragStart = input.Position
            startPos = frame.Position
            input.Changed:Connect(function()
                if input.UserInputState == Enum.UserInputState.End then
                    dragging = false
                end
            end)
        end
    end)
    UserInputService.InputChanged:Connect(function(input)
        if dragging
            and (input.UserInputType == Enum.UserInputType.MouseMovement
                or input.UserInputType == Enum.UserInputType.Touch) then
            local delta = input.Position - dragStart
            frame.Position = UDim2.new(
                startPos.X.Scale, startPos.X.Offset + delta.X,
                startPos.Y.Scale, startPos.Y.Offset + delta.Y
            )
        end
    end)
    local sub = Instance.new("TextLabel")
    sub.Size = UDim2.new(1, -40, 0, 20) sub.Position = UDim2.new(0, 20, 0, 60)
    sub.BackgroundTransparency = 1
    sub.Text = "Enter your key to continue" sub.TextSize = 13
    sub.TextColor3 = Color3.fromRGB(154, 154, 148)
    sub.TextXAlignment = Enum.TextXAlignment.Left
    sub.Parent = frame
    local box = Instance.new("TextBox")
    box.Size = UDim2.new(1, -40, 0, 42) box.Position = UDim2.new(0, 20, 0, 86)
    box.PlaceholderText = "Paste your key" box.Text = ""
    box.Font = Enum.Font.Code box.TextSize = 14
    box.BackgroundColor3 = Color3.fromRGB(10, 12, 10)
    box.TextColor3 = Color3.fromRGB(245, 245, 243)
    box.ClearTextOnFocus = false
    box.Parent = frame
    local boxCorner = Instance.new("UICorner") boxCorner.CornerRadius = UDim.new(0, 10) boxCorner.Parent = box
    local btn = Instance.new("TextButton")
    btn.Size = UDim2.new(1, -40, 0, 44) btn.Position = UDim2.new(0, 20, 0, 140)
    btn.Text = "Verify" btn.Font = Enum.Font.GothamBold btn.TextSize = 16
    btn.BackgroundColor3 = Color3.fromRGB(22, 163, 74)
    btn.TextColor3 = Color3.fromRGB(4, 20, 10)
    btn.AutoButtonColor = true
    btn.Parent = frame
    local bc = Instance.new("UICorner") bc.CornerRadius = UDim.new(0, 10) bc.Parent = btn
    local getBtn = Instance.new("TextButton")
    getBtn.Size = UDim2.new(1, -40, 0, 36) getBtn.Position = UDim2.new(0, 20, 0, 192)
    getBtn.Text = "Get Key" getBtn.Font = Enum.Font.GothamBold getBtn.TextSize = 14
    getBtn.BackgroundTransparency = 1
    getBtn.TextColor3 = Color3.fromRGB(34, 197, 94)
    getBtn.Parent = frame
    local getCorner = Instance.new("UICorner") getCorner.CornerRadius = UDim.new(0, 10) getCorner.Parent = getBtn
    local getStroke = Instance.new("UIStroke") getStroke.Color = Color3.fromRGB(22, 163, 74) getStroke.Thickness = 1 getStroke.Parent = getBtn
    local status = Instance.new("TextLabel")
    status.Size = UDim2.new(1, -40, 0, 34) status.Position = UDim2.new(0, 20, 0, 236)
    status.BackgroundTransparency = 1
    status.Text = "" status.TextSize = 13
    status.TextColor3 = Color3.fromRGB(154, 154, 148)
    status.TextWrapped = true
    status.Parent = frame
    local foot = Instance.new("TextLabel")
    foot.Size = UDim2.new(1, -40, 0, 20) foot.Position = UDim2.new(0, 20, 0, 300)
    foot.BackgroundTransparency = 1
    foot.Text = "Drag the top bar to move • x to close" foot.TextSize = 11
    foot.TextColor3 = Color3.fromRGB(110, 110, 105)
    foot.Parent = frame
    local function friendly(err)
        if err == "invalid_key" then return "Invalid key"
        elseif err == "expired" then return "Key expired — get a new one"
        elseif err == "hwid_mismatch" then return "Key is locked to another device"
        elseif err == "rate_limited" then return "Too fast — slow down"
        elseif err == "request_failed" or err == "bad_response" then return "Network error — retry"
        else return tostring(err) end
    end
    getBtn.MouseButton1Click:Connect(function()
        local link = Vine.KeyPageLink()
        if setclipboard then
            local okClip = pcall(setclipboard, link)
            status.Text = okClip and "Key link copied — open it in your browser." or "Copy failed."
        else
            status.Text = link
        end
    end)
    btn.MouseButton1Click:Connect(function()
        local now = os.clock()
        if now < cooldownUntil then
            status.Text = "Wait " .. tostring(math.ceil(cooldownUntil - now)) .. "s…"
            return
        end
        cooldownUntil = now + (Vine.Config.CooldownSeconds or 5)
        btn.Text = "Checking…"
        status.Text = ""
        task.spawn(function()
            local ok, info = Vine.Verify(box.Text)
            if ok then
                gui:Destroy()
                if callback then
                    local okCb, cbErr = pcall(callback, true)
                    if not okCb then warn("Vine: callback error: " .. tostring(cbErr)) end
                end
            else
                if info == "rate_limited" then
                    cooldownUntil = os.clock() + (Vine.Config.RateLimitedCooldown or 30)
                end
                btn.Text = "Verify"
                status.Text = friendly(info)
            end
        end)
    end)
    gui.Parent = (gethui and gethui() or game.CoreGui)
    return gui
end

return Vine
