local addonName, SinsToolkit = ...

if SinsToolkit.UIProvider == "ElvUI" then return end

-- This file defines the AceConfig options table used by standalone mode.

function SinsToolkit:GetOptionsTable(provider)
    local isElvUI = provider == "ElvUI"
    local E
    if isElvUI then E = unpack(_G.ElvUI) end

    -- Local state for options panel (shared between re-renders)
    local uiErrorListCache
    local errorSearchString = ""
    local currentExportName = ""
    local currentExportString = ""
    local profileVaultBackupName = ""
    local selectedProfileVaultBackup = ""
    local cvarSearchString = ""
    local cvarModifiedOnly = false
    local selectedCVar = ""
    local cvarPendingValue = ""
    local uiErrorGroupCache = {}

    -- Helper functions (some adapted from ElvUI.lua)
    local function EnsureDB()
        if SinsToolkit.EnsureDB then SinsToolkit.EnsureDB() end
    end

    local function GetToolkitValue(key)
        EnsureDB()
        return SinsToolkitDB and SinsToolkitDB[key]
    end

    local function NotifyChange()
        if isElvUI and E.Libs.AceConfigRegistry then
            E.Libs.AceConfigRegistry:NotifyChange("ElvUI")
        elseif not isElvUI then
            local registry = LibStub and LibStub("AceConfigRegistry-3.0", true)
            if registry then
                registry:NotifyChange(addonName)
            end
        end
    end

    local function SetToolkitValue(key, value)
        EnsureDB()
        SinsToolkitDB[key] = value
        if SinsToolkit.TriggerUpdate then SinsToolkit.TriggerUpdate(key) end
        NotifyChange()
    end

    local function GetUIErrorList()
        if not uiErrorListCache then
            uiErrorListCache = {}
            if GetGameMessageInfo then
                for i = 1, 1600 do
                    local stringID = GetGameMessageInfo(i)
                    local text = stringID and _G[stringID]
                    if stringID and stringID:match("^ERR_") and text and text ~= "" then
                        uiErrorListCache[#uiErrorListCache + 1] = { key = stringID, text = text, searchText = (stringID .. " " .. text):lower() }
                    end
                end
            end
            table.sort(uiErrorListCache, function(a, b) return a.text < b.text end)
        end
        return uiErrorListCache
    end

    local GEAR_SLOT_VALUES = {
        [INVSLOT_HEAD] = HEADSLOT, [INVSLOT_NECK] = NECKSLOT, [INVSLOT_SHOULDER] = SHOULDERSLOT,
        [INVSLOT_BACK] = BACKSLOT, [INVSLOT_CHEST] = CHESTSLOT, [INVSLOT_WRIST] = WRISTSLOT,
        [INVSLOT_HAND] = HANDSSLOT, [INVSLOT_WAIST] = WAISTSLOT, [INVSLOT_LEGS] = LEGSSLOT,
        [INVSLOT_FEET] = FEETSLOT, [INVSLOT_FINGER1] = FINGER0SLOT, [INVSLOT_FINGER2] = FINGER1SLOT,
        [INVSLOT_TRINKET1] = TRINKET0SLOT, [INVSLOT_TRINKET2] = TRINKET1SLOT, [INVSLOT_MAINHAND] = MAINHANDSLOT,
        [INVSLOT_OFFHAND] = SECONDARYHANDSLOT,
    }

    local function SanitizeExportString(value)
        return (value or ""):gsub("[\r\n\t]+", "")
    end

    local function GetProfileVaultBackupValues()
        EnsureDB()
        local values = { [""] = "None" }
        if not SinsToolkit.ProfileVault then return values end
        for _, backup in ipairs(SinsToolkit.ProfileVault:GetBackups()) do
            local label = ("%s - %s - %s"):format(backup.name or "Backup", backup.className or backup.classToken or "Unknown", backup.createdAt or "Unknown date")
            values[backup.id] = label
        end
        return values
    end

    local function GetSelectedProfileVaultBackup()
        EnsureDB()
        return selectedProfileVaultBackup ~= "" and SinsToolkitDB.profileVaultBackups and SinsToolkitDB.profileVaultBackups[selectedProfileVaultBackup] or nil
    end

    local function GetProfileVaultSummary()
        local backup = GetSelectedProfileVaultBackup()
        if not backup then return "Select a saved backup to view its details." end
        local actionCount = backup.actions and #backup.actions or 0
        local accountMacroCount = backup.macros and backup.macros.account and #backup.macros.account or 0
        local characterMacroCount = backup.macros and backup.macros.character and #backup.macros.character or 0
        local bindingCount = backup.bindings and #backup.bindings or 0
        return ("Backup: %s\nCharacter: %s\nRealm: %s\nClass: %s\nDate: %s\n\nIncludes: %d action slots, %d account macros, %d character macros, %d key bindings."):format(
            backup.name or "Backup", backup.character or "Unknown", backup.realm or "Unknown",
            backup.className or backup.classToken or "Unknown", backup.createdAt or "Unknown",
            actionCount, accountMacroCount, characterMacroCount, bindingCount)
    end

    local function GetCVarValues()
        if not SinsToolkit.CVars then return { [""] = "CVar browser unavailable" } end
        return SinsToolkit.CVars:GetValues(cvarSearchString, cvarModifiedOnly)
    end

    local function GetSelectedCVarInfo()
        if not SinsToolkit.CVars or selectedCVar == "" then return nil end
        return SinsToolkit.CVars:GetInfo(selectedCVar)
    end

    local function GetSelectedCVarSummary()
        if not SinsToolkit.CVars then return "The CVar browser module is not loaded." end
        return SinsToolkit.CVars:GetSummary(selectedCVar)
    end

    local function GetGearProviderStatus()
        local pawnActive = SinsToolkit.IsPawnAvailable and SinsToolkit.IsPawnAvailable()
        local classCodexActive = SinsToolkit.IsClassCodexAvailable and SinsToolkit.IsClassCodexAvailable()
        return ("|cffd8a84aScoring Providers|r\nPawn: %s\nClassCodex: %s\nMode: %s"):format(
            pawnActive and "|cff00ff00Active|r" or "|cffff4040Inactive|r",
            classCodexActive and "|cff00ff00Active|r" or "|cffff4040Inactive|r",
            SinsToolkit.GetGearProviderModeText and SinsToolkit.GetGearProviderModeText() or "Unavailable.")
    end

    local function UpdateErrorToggles(args)
        for k in pairs(args) do
            if type(k) == "string" and k:match("^ERR_") then args[k] = nil end
        end
        local search = (errorSearchString or ""):lower()
        local blocked = SinsToolkitDB and SinsToolkitDB.blockedUIErrors or {}
        local sorted = uiErrorGroupCache[search]
        if not sorted then
            local groupedByText = {}
            sorted = {}
            for _, err in ipairs(GetUIErrorList()) do
                if search == "" or err.searchText:find(search, 1, true) then
                    local grouped = groupedByText[err.text]
                    if grouped then
                        grouped.keys[#grouped.keys + 1] = err.key
                        grouped.searchText = grouped.searchText .. " " .. err.searchText
                    else
                        grouped = { key = err.key, keys = { err.key }, text = err.text, searchText = err.searchText }
                        groupedByText[err.text] = grouped
                        sorted[#sorted + 1] = grouped
                    end
                end
            end
            table.sort(sorted, function(a, b) return a.text < b.text end)
            uiErrorGroupCache[search] = sorted
        end
        table.sort(sorted, function(a, b)
            local function IsAnyBlocked(err)
                for _, key in ipairs(err.keys) do if blocked[key] == true then return true end end
                return false
            end
            local aBlocked, bBlocked = IsAnyBlocked(a), IsAnyBlocked(b)
            if aBlocked ~= bBlocked then return aBlocked end
            return a.text < b.text
        end)
        for i, err in ipairs(sorted) do
            args[err.key] = {
                type = "toggle", name = err.text, order = 1000 + i, width = "double",
                get = function()
                    EnsureDB()
                    if not SinsToolkitDB.blockedUIErrors then return false end
                    for _, key in ipairs(err.keys) do if SinsToolkitDB.blockedUIErrors[key] then return true end end
                    return false
                end,
                set = function(_, value)
                    EnsureDB()
                    if not SinsToolkitDB.blockedUIErrors then SinsToolkitDB.blockedUIErrors = {} end
                    for _, key in ipairs(err.keys) do SinsToolkitDB.blockedUIErrors[key] = value or nil end
                    if SinsToolkit.TriggerUpdate then SinsToolkit.TriggerUpdate("blockedUIErrors") end
                    UpdateErrorToggles(args)
                    NotifyChange()
                end,
            }
        end
    end

    if C_Timer then
        C_Timer.After(1, GetUIErrorList)
        C_Timer.After(5, function() if SinsToolkit.CVars then SinsToolkit.CVars:GetValues("", false) end end)
    end

    local BRAND_LOGO = "Interface\\AddOns\\SinsToolkit\\Media\\Logo.tga"
    local BRAND_BANNER = "Interface\\AddOns\\SinsToolkit\\Media\\Banner.tga"
    local BANNER_ASPECT = 640 / 213
    local BANNER_MIN_HEIGHT = 150
    local BANNER_MAX_HEIGHT = 260

    local function GetBannerSize()
        local parentWidth = (isElvUI and E.Libs.AceConfigDialog.OpenFrames.ElvUI and E.Libs.AceConfigDialog.OpenFrames.ElvUI.frame:GetWidth()) or 800
        parentWidth = parentWidth or (UIParent and UIParent:GetWidth()) or 1280
        local width = math.floor(math.max(520, math.min(920, parentWidth - (isElvUI and 420 or 180))))
        local height = math.floor(math.max(BANNER_MIN_HEIGHT, math.min(BANNER_MAX_HEIGHT, width / BANNER_ASPECT)))
        width = math.floor(height * BANNER_ASPECT)
        return width, height
    end

    local function GradientText(text)
        local startColor = { 126, 79, 168 }; local endColor = { 246, 239, 255 }
        local out, visibleIndex, visibleCount = {}, 0, 0
        for i = 1, #text do if text:sub(i, i) ~= " " then visibleCount = visibleCount + 1 end end
        for i = 1, #text do
            local char = text:sub(i, i)
            if char == " " then out[#out + 1] = char
            else
                visibleIndex = visibleIndex + 1
                local t = visibleCount > 1 and ((visibleIndex - 1) / (visibleCount - 1)) or 1
                local r = math.floor(startColor[1] + ((endColor[1] - startColor[1]) * t) + 0.5)
                local g = math.floor(startColor[2] + ((endColor[2] - startColor[2]) * t) + 0.5)
                local b = math.floor(startColor[3] + ((endColor[3] - startColor[3]) * t) + 0.5)
                out[#out + 1] = ("|cff%02x%02x%02x%s|r"):format(r, g, b, char)
            end
        end
        return table.concat(out)
    end

    local function PageIntro(title, body)
        return { type = "description", name = "|cffb868dc" .. title .. "|r\n|cffbca7d6" .. body .. "|r", fontSize = "medium", order = 0 }
    end

    if not isElvUI then
        local version = (C_AddOns and C_AddOns.GetAddOnMetadata) and C_AddOns.GetAddOnMetadata(addonName, "Version") or GetAddOnMetadata(addonName, "Version") or "Unknown"
        local launcher = {
            type = "group",
            name = "|T" .. BRAND_LOGO .. ":16:16|t " .. GradientText("Sin's Toolkit: QOL"),
            args = {
                desc = { type = "description", name = "Quality-of-life enhancements for World of Warcraft.\n\nVersion: " .. version .. "\nProvider: Standalone\nStatus: Ready\n", fontSize = "medium", order = 1 },
                openApp = {
                    type = "execute",
                    name = "Open Sin's Toolkit",
                    desc = "Launch the Sin's Toolkit standalone application.",
                    func = function()
                        if SettingsPanel and SettingsPanel:IsShown() then SettingsPanel:Hide() end
                        if InterfaceOptionsFrame and InterfaceOptionsFrame:IsShown() then InterfaceOptionsFrame:Hide() end
                        if SinsToolkit.Window then SinsToolkit.Window:Show() end
                    end,
                    order = 2,
                },
                resetMinimap = {
                    type = "execute",
                    name = "Reset Minimap Position",
                    desc = "Reset the minimap button to its default position.",
                    func = function()
                        if SinsToolkitDB and SinsToolkitDB.minimap then
                            SinsToolkitDB.minimap.minimapPos = 225
                            local DBIcon = LibStub and LibStub("LibDBIcon-1.0", true)
                            if DBIcon then DBIcon:Refresh(addonName, SinsToolkitDB.minimap) end
                            print("|cff9b59b6[Sin's Toolkit: QOL]|r Minimap button position reset.")
                        end
                    end,
                    order = 3,
                },
            },
        }
        return launcher
    end

    local function GetChangelogText()
        return [[|cffd8a84aVersion 2.0 - June 12, 2026|r
- Converted Sin's Toolkit: QOL into one addon that supports both modes: ElvUI plugin mode when ElvUI is installed, and standalone mode when ElvUI is not installed.
- Added provider guards so the standalone window does not activate while ElvUI is running.
- Restored standalone Information and Changelog pages.
- Restored the full standalone Gear Scanner options for loot rolls, soulbinding, Pawn use, tooltips, minimum score, unbound upgrades, max-level behavior, and locked slots.
- Split standalone settings into clear pages while preserving the existing SinsToolkitDB saved-variable keys from the ElvUI plugin version.
- Centered and resized the standalone Information banner dynamically as the Toolkit window changes size.
- Updated the Information panel to explain the dual ElvUI/standalone behavior and minimap button controls.

|cffd8a84aVersion 1.1.56 - June 11, 2026|r
- Fixed a WoW Lua parser warning caused by the in-game changelog being too deeply nested inside the ElvUI options table.
- Kept the recent release notes visible while simplifying the changelog expression so the options panel loads cleanly.

|cffd8a84aVersion 1.1.55 - June 11, 2026|r
- Made Teleport Hub favorite controls much more visible with a clear + Fav / Fav button on each entry.
- Added empty Favorites helper text and adjusted Teleport Hub spacing so favorite buttons do not crowd teleport labels.

|cffd8a84aVersion 1.1.54 - June 11, 2026|r
- Fixed the ElvUI options validation error caused by dynamic banner image sizing callbacks.
- Banner dimensions are now calculated before options registration so AceConfig receives valid numeric image sizes.

|cffd8a84aVersion 1.1.53 - June 11, 2026|r
- Fixed Teleport Hub Favorites so the section is always visible, including an empty Favorites (0) state.
- Refined the Information banner sizing so the existing artwork scales with the ElvUI options panel while preserving aspect ratio and padding.

|cffd8a84aVersion 1.1.52 - June 11, 2026|r
- Fixed a Delve runtime error caused by parsing Blizzard secret unit GUID strings during Automate Gossip checks.
- Automate Gossip now safely skips NPC allow-list parsing when the interaction target is protected by the client.
- Hardened similar Wowhead Quick Link unit/name parsing paths against secret values without forcing protected strings through tostring().

|cffd8a84aVersion 1.1.51 - June 10, 2026|r
- Polished Teleport Hub with Favorites, Recently Used, remembered collapsible sections, and Expand/Collapse controls.
- Added a Bug Catcher option to only show the minimap button when errors exist, enabled by default for new installs while preserving existing users.
- Updated the Information tab so it documents Advanced Settings, provider status, Fallback Mode, Bug Catcher self-test, Profile Vault confirmations, and Teleport Hub's new layout tools.
- Softened the purple sidebar gradient and adjusted the banner dimensions to reduce cramped or distorted presentation in ElvUI options.
- Raised Profile Vault confirmation dialogs above addon windows so restore/delete prompts are harder to miss.

|cffd8a84aVersion 1.1.50 - June 10, 2026|r
- Centralized saved variable defaults in Core and added database versioning for safer future migrations.
- Added Profile Vault confirmation prompts before restore or delete actions touch action bars, macros, or keybindings.
- Reworked UI error filtering to use a dedicated forwarding frame instead of replacing UIErrorsFrame's OnEvent script.
- Added optional BugSack compatibility mode, off by default for new installs and preserved for existing users through migration.
- Added /stkbug test and a Bug Catcher self-test button.
- Added Gear Scanner provider status and a top-level Advanced settings page for higher-risk options.]]
    end

    local bannerWidth, bannerHeight = GetBannerSize()

    local options = {
        type = "group",
        name = "|T" .. BRAND_LOGO .. ":16:16|t " .. GradientText("Sin's Toolkit: QOL"),
        childGroups = "tree",
        args = {
            info = {
                type = "group", name = GradientText("Information"), order = 1,
                args = {
                    brand = { type = "description", name = "\n", image = BRAND_BANNER, imageWidth = bannerWidth, imageHeight = bannerHeight, fontSize = "medium", order = 0 },
                    tagline = { type = "description", name = "|cffbca7d6Provider: Standalone\nStatus: Ready|r\n", fontSize = "medium", order = 0.5 },
                    header = {
                        type = "description",
                        name = "|cff9b59b6Feature Overview|r\n\nWelcome to Sin's Toolkit: QOL. The goal is simple: reduce addon clutter, keep useful options easy to find, and keep expanding the toolkit as World of Warcraft changes.\n\n- |cffd8a84aGear Scanner:|r Scans bag gear automatically or with |cff3498db/stk scan|r, compares upgrades using Pawn when installed or ClassCodex fallback priorities, respects class armor types, supports locked slots, tooltip scores, won't-equip reasons, upgrade explanations, unbound-item protection, max-level disable, silent mode, and configurable loot-roll/soulbind confirmation options. The provider status panel explains whether Pawn, ClassCodex, or basic fallback scoring is being used. Inspired by AutoGear.\n\n- |cffd8a84aUI Tweaks & Automation:|r Adds No Auto Close behavior, Auto Gossip, Auto Unshift for Druids, and Druid Auto Flight Form in one place. Auto Unshift can be helpful if you use GSE, also known as Gnome Sequencer.\n\n- |cffd8a84aProfessions & Lookups:|r Hold your selected modifier over an eligible bag item to show the secure overlay, then click to mill, prospect, disenchant, or open supported items. Also includes copyable Wowhead, Armory, and Raider.IO lookup popups. Inspired by Molinari and Wowhead Quick Link.\n\n|cffd8a84aAvailable Commands:|r\n- |cff3498db/stk status|r - Prints feature and automation status.\n- |cff3498db/stk scan|r - Forces a manual Gear Scanner scan.\n- |cff3498db/stkid|r - Identifies the frame under your cursor.\n- |cff3498db/stkbug test|r - Captures a safe Bug Catcher self-test entry.\n- |cff3498db/stktp|r - Opens or closes Teleport Hub.",
                        fontSize = "medium", order = 1,
                    },
                },
            },
            gear = {
                type = "group", name = GradientText("Gear Scanner"), order = 4,
                args = {
                    intro = PageIntro("Gear Scanner", "Automatic and manual gear scanning powered by Pawn when installed, with ClassCodex stat priorities as fallback."),
                    providerStatus = { type = "description", name = GetGearProviderStatus, fontSize = "medium", order = 0.5 },
                    enabled = { type = "toggle", name = "Enable Gear Scanner", desc = "Master switch for Gear Scanner. When enabled, Sin's Toolkit can scan bag items, compare them against your equipped gear, and equip upgrades using Pawn when installed or ClassCodex fallback scoring.", order = 1,
                        disabled = function() return SinsToolkit.IsGearScannerProviderAvailable and not SinsToolkit.IsGearScannerProviderAvailable() end,
                        get = function() return GetToolkitValue("gearScannerEnabled") end,
                        set = function(_, v) SetToolkitValue("gearScannerEnabled", v) end,
                    },
                    maxLevel = { type = "toggle", name = "Disable at Max Level", desc = "Turns Gear Scanner off automatically when your character reaches the current expansion level cap. This keeps leveling automation from interfering with max-level gearing decisions.", order = 2,
                        get = function() return GetToolkitValue("gearScannerMaxLevelDisable") end,
                        set = function(_, v) SetToolkitValue("gearScannerMaxLevelDisable", v) end,
                    },
                    silent = { type = "toggle", name = "Silent Mode", desc = "Suppresses Gear Scanner chat messages when items are equipped, scans are run, or loot-roll actions are taken.", order = 3,
                        get = function() return GetToolkitValue("gearScannerSilent") end,
                        set = function(_, v) SetToolkitValue("gearScannerSilent", v) end,
                    },
                    scan = { type = "execute", name = "Force Gear Scan", desc = "Runs an immediate bag scan using the current Gear Scanner settings. This is the same action as /stk scan.", order = 4,
                        func = function() if SinsToolkit.ForceGearScan then SinsToolkit.ForceGearScan() end end,
                    },
                    behaviorHeader = { type = "header", name = "Advanced Auto-Equip Behavior", order = 5 },
                    scanOnLoot = { type = "toggle", name = "Scan When Bags Change", desc = "Automatically scans your bags after loot or bag changes. Turn this off if you only want Gear Scanner to run when you click Force Gear Scan or type /stk scan.", order = 6,
                        get = function() return GetToolkitValue("gearScannerScanOnLoot") end,
                        set = function(_, v) SetToolkitValue("gearScannerScanOnLoot", v) end,
                    },
                    allowUnbound = { type = "toggle", name = "Allow Unbound Upgrades", desc = "Allows Gear Scanner to equip unbound upgrades from your bags. Leave this off if you want BoE items left untouched for selling, trading, mailing, or auctioning.", order = 7,
                        get = function() return GetToolkitValue("gearScannerAllowUnbound") end,
                        set = function(_, v) SetToolkitValue("gearScannerAllowUnbound", v) end,
                    },
                    minimumUpgrade = { type = "range", name = "Minimum Upgrade Score", desc = "Requires a new item to beat your equipped item by at least this many score points before it is equipped. Raise this if you only want larger upgrades equipped.", order = 8, min = 0, max = 500, step = 5,
                        get = function() return GetToolkitValue("gearScannerMinimumUpgrade") or 0 end,
                        set = function(_, v) SetToolkitValue("gearScannerMinimumUpgrade", v) end,
                    },
                    pawnHeader = { type = "header", name = "Pawn Preference", order = 9 },
                    usePawn = { type = "toggle", name = "Use Pawn When Installed", desc = "When Pawn is installed, Sin's Toolkit asks Pawn's own upgrade advisor first. If Pawn is missing, disabled, throttled, or cannot evaluate an item, Gear Scanner falls back to ClassCodex stat priorities.", order = 9.1,
                        get = function() return GetToolkitValue("gearScannerUsePawn") end,
                        set = function(_, v) SetToolkitValue("gearScannerUsePawn", v) end,
                    },
                    lootRollHeader = { type = "header", name = "Loot Rolls", order = 9.2 },
                    autoRollGreensBopBlues = { type = "toggle", name = "Automatically Roll on Greens and BoP Blues", desc = "Automatically handles green gear and blue bind-on-pickup gear loot rolls. If an item is an upgrade, Sin's Toolkit rolls Need unless Never Auto-Need is enabled. Non-upgrades roll Greed when Greed is available.", order = 9.3,
                        get = function() return GetToolkitValue("gearScannerAutoRollGreensBopBlues") end,
                        set = function(_, v) SetToolkitValue("gearScannerAutoRollGreensBopBlues", v) end,
                    },
                    autoRollBoeBlues = { type = "toggle", name = "Automatically Roll on BoE Blues", desc = "Automatically handles blue bind-on-equip gear loot rolls. Use this carefully if you prefer to sell, trade, or auction BoE blue items instead of treating them like leveling upgrades.", order = 9.4,
                        get = function() return GetToolkitValue("gearScannerAutoRollBoeBlues") end,
                        set = function(_, v) SetToolkitValue("gearScannerAutoRollBoeBlues", v) end,
                    },
                    autoRollEpics = { type = "toggle", name = "Automatically Roll on Epics", desc = "Automatically handles epic gear loot rolls. This is off by default because many players want manual control over epic loot.", order = 9.5,
                        get = function() return GetToolkitValue("gearScannerAutoRollEpics") end,
                        set = function(_, v) SetToolkitValue("gearScannerAutoRollEpics", v) end,
                    },
                    rollNonGearLoot = { type = "toggle", name = "Roll on Non-Gear Loot", desc = "Automatically rolls Greed on handled non-gear loot. This does not evaluate upgrades because the item is not equippable gear.", order = 9.6,
                        get = function() return GetToolkitValue("gearScannerRollNonGearLoot") end,
                        set = function(_, v) SetToolkitValue("gearScannerRollNonGearLoot", v) end,
                    },
                    neverAutoNeed = { type = "toggle", name = "Never Auto-Need; Only Greed", desc = "Prevents Sin's Toolkit from rolling Need automatically. When this is enabled, upgrade candidates will roll Greed instead of Need when Greed is available.", order = 9.7,
                        get = function() return GetToolkitValue("gearScannerNeverAutoNeed") end,
                        set = function(_, v) SetToolkitValue("gearScannerNeverAutoNeed", v) end,
                    },
                    bindingHeader = { type = "header", name = "Soulbind Confirmation", order = 9.8 },
                    autoConfirmBindOther = { type = "toggle", name = "Confirm Soulbinding for Non-Blue/Non-Epic Gear", desc = "Automatically confirms the soulbind popup when Gear Scanner equips an upgrade below blue quality. Leave this off if you want to manually confirm binding items.", order = 9.9,
                        get = function() return GetToolkitValue("gearScannerAutoConfirmBindOther") end,
                        set = function(_, v) SetToolkitValue("gearScannerAutoConfirmBindOther", v) end,
                    },
                    autoConfirmBindBlues = { type = "toggle", name = "Confirm Soulbinding for Blues", desc = "Automatically confirms the soulbind popup when Gear Scanner equips a blue upgrade. Leave this off if you want manual control over binding blue items.", order = 9.91,
                        get = function() return GetToolkitValue("gearScannerAutoConfirmBindBlues") end,
                        set = function(_, v) SetToolkitValue("gearScannerAutoConfirmBindBlues", v) end,
                    },
                    autoConfirmBindEpics = { type = "toggle", name = "Confirm Soulbinding for Epics", desc = "Automatically confirms the soulbind popup when Gear Scanner equips an epic upgrade. This is off by default because epic items are usually worth reviewing manually.", order = 9.92,
                        get = function() return GetToolkitValue("gearScannerAutoConfirmBindEpics") end,
                        set = function(_, v) SetToolkitValue("gearScannerAutoConfirmBindEpics", v) end,
                    },
                    tooltipsHeader = { type = "header", name = "Tooltips", order = 10 },
                    scoreTooltips = { type = "toggle", name = "Show Item Score", desc = "Adds a Sin's Toolkit gear score line to item tooltips. When Use Pawn When Installed is enabled and Pawn is available, Pawn upgrade candidates are prioritized; otherwise scores use ClassCodex stat priorities.", order = 11,
                        get = function() return GetToolkitValue("gearScannerScoreTooltips") end,
                        set = function(_, v) SetToolkitValue("gearScannerScoreTooltips", v) end,
                    },
                    reasonTooltips = { type = "toggle", name = "Show Won't-Equip Reasons", desc = "Adds a tooltip note explaining why an item will or will not equip, such as not being an upgrade, being blocked by a locked slot, Pawn fallback behavior, or being an upgrade candidate.", order = 12,
                        get = function() return GetToolkitValue("gearScannerReasonsTooltips") end,
                        set = function(_, v) SetToolkitValue("gearScannerReasonsTooltips", v) end,
                    },
                    lockHeader = { type = "header", name = "Locked Slots", order = 20 },
                    lockSlots = { type = "toggle", name = "Lock Gear Slots", desc = "Enables the locked slot list below. Locked slots are still scored, but Gear Scanner will not replace items in those slots.", order = 21,
                        get = function() return GetToolkitValue("gearScannerLockSlots") end,
                        set = function(_, v) SetToolkitValue("gearScannerLockSlots", v) end,
                    },
                    lockedSlots = { type = "multiselect", name = "Slots to Lock", desc = "Choose the equipment slots Gear Scanner should never replace during automatic scans or manual /stk scan checks.", order = 22, values = GEAR_SLOT_VALUES,
                        disabled = function() return not GetToolkitValue("gearScannerLockSlots") end,
                        get = function(_, key)
                            EnsureDB()
                            return SinsToolkitDB.gearScannerLockedSlots and SinsToolkitDB.gearScannerLockedSlots[key] == true
                        end,
                        set = function(_, key, value)
                            EnsureDB()
                            SinsToolkitDB.gearScannerLockedSlots = SinsToolkitDB.gearScannerLockedSlots or {}
                            SinsToolkitDB.gearScannerLockedSlots[key] = value or nil
                        end,
                    },
                },
            },
            tweaks = {
                type = "group", name = GradientText("UI Tweaks & Automation"), order = 5,
                args = {
                    intro = PageIntro("UI Tweaks & Automation", "Small interface behavior changes and optional automation helpers for repeated interactions and Druid form handling."),
                    panels = { type = "toggle", name = "No Auto Close Panels", desc = "Uses Sin's Toolkit No Auto Close behavior so standard Blizzard UI panels can stay open together. Reload after disabling.", order = 2,
                        get = function() return GetToolkitValue("keepPanelsOpen") end,
                        set = function(_, v) SetToolkitValue("keepPanelsOpen", v) end,
                    },
                    automateGossip = { type = "toggle", name = "Automate Gossip", desc = "Automatically selects safe single-option gossip while avoiding city direction, trainer, bank, auction house, and storyteller-style gossip.", order = 3,
                        get = function() return GetToolkitValue("automateGossip") end,
                        set = function(_, v) SetToolkitValue("automateGossip", v) end,
                    },
                    automateGossipModifier = { type = "select", name = "Gossip Bypass Modifier", desc = "Hold this modifier key to temporarily bypass auto-gossip.", order = 4, values = { NONE = "None", SHIFT = "Shift", CTRL = "Ctrl", ALT = "Alt" },
                        get = function() return GetToolkitValue("automateGossipModifier") or "SHIFT" end,
                        set = function(_, v) SetToolkitValue("automateGossipModifier", v) end,
                    },
                    gseAutoUnshift = { type = "toggle", name = "Auto Unshift for Druids", desc = "Disables auto-unshift in combat for Feral/Guardian Druids. This can be helpful if you use GSE, also known as Gnome Sequencer. While enabled, Sin's Toolkit hides the SPELL_FAILED_NOT_SHAPESHIFT UI error. When unchecked, that specific error returns to Blizzard's default display behavior. Requires GSE.", order = 5,
                        disabled = function() return SinsToolkit.IsGSEAvailable and not SinsToolkit.IsGSEAvailable() end,
                        get = function() return GetToolkitValue("gseAutoUnshift") end,
                        set = function(_, v) SetToolkitValue("gseAutoUnshift", v) end,
                    },
                    druidAutoFlightForm = { type = "toggle", name = "Druid Auto Flight Form", desc = "When you are a Druid in ground Travel Form and move into an outdoor flyable area, Sin's Toolkit QOL cancels ground Travel Form so the client can move you into Flight Form on your next travel-form action. This is separate from Auto Unshift for Druids and never runs in combat, instances, challenge modes, while flying, or while submerged.", order = 6,
                        disabled = function() local _, classToken = UnitClass("player"); return classToken ~= "DRUID" end,
                        get = function() return GetToolkitValue("druidAutoFlightForm") end,
                        set = function(_, v) SetToolkitValue("druidAutoFlightForm", v) end,
                    },
                },
            },
            uiErrors = {
                type = "group", name = GradientText("UI Errors"), order = 6,
                args = {
                    intro = PageIntro("UI Errors", "Choose which red UI error messages Sin's Toolkit should block."),
                    hideOutOfRangeErrors = { type = "toggle", name = "Enable UI Error Filtering", desc = "Enable or disable the filtering of UI error messages entirely.", order = 1,
                        get = function() return GetToolkitValue("hideOutOfRangeErrors") end,
                        set = function(_, v) SetToolkitValue("hideOutOfRangeErrors", v) end,
                    },
                    search = { type = "input", name = "Search Errors", desc = "Filter the error list below. Text updates dynamically.", order = 2, width = "full",
                        get = function() return errorSearchString end,
                        set = function(_, value)
                            errorSearchString = value
                            UpdateErrorToggles(options.args.uiErrors.args)
                            NotifyChange()
                        end,
                    },
                    listHeader = { type = "header", name = "Blocked Messages", order = 999 },
                },
            },
            cvars = {
                type = "group", name = GradientText("CVars"), order = 7,
                args = {
                    intro = { type = "description", name = "Search live client CVars, compare current values against Blizzard defaults, and edit selected values. Modified CVars appear |cff55ff55green|r in the selector. The list is validated through the WoW client at runtime so removed or renamed CVars are skipped instead of breaking the panel.", order = 1, fontSize = "medium" },
                    search = { type = "input", name = "Search CVars", desc = "Search by CVar name, category, or client-provided description.", order = 2, width = "full",
                        get = function() return cvarSearchString end,
                        set = function(_, value) cvarSearchString = value or ""; NotifyChange() end,
                    },
                    modifiedOnly = { type = "toggle", name = "Show Modified Only", desc = "Only show CVars where the current value differs from the client default.", order = 3,
                        get = function() return cvarModifiedOnly end,
                        set = function(_, value)
                            cvarModifiedOnly = value
                            if selectedCVar ~= "" and SinsToolkit.CVars then
                                local values = SinsToolkit.CVars:GetValues(cvarSearchString, cvarModifiedOnly)
                                if not values[selectedCVar] then
                                    selectedCVar = ""
                                    cvarPendingValue = ""
                                end
                            end
                            NotifyChange()
                        end,
                    },
                    refresh = {
                        type = "execute",
                        name = "Refresh List",
                        desc = "Refresh the CVar list and current/default values from the WoW client.",
                        order = 4,
                        func = function()
                            if SinsToolkit.CVars then
                                SinsToolkit.CVars:Refresh()
                                local info = GetSelectedCVarInfo()
                                cvarPendingValue = info and tostring(info.value or "") or ""
                            end
                            NotifyChange()
                        end,
                    },
                    selector = {
                        type = "select",
                        name = "CVar",
                        desc = "Choose a CVar to inspect or edit.",
                        order = 5,
                        width = "full",
                        values = GetCVarValues,
                        get = function() return selectedCVar end,
                        set = function(_, value)
                            selectedCVar = value or ""
                            local info = GetSelectedCVarInfo()
                            cvarPendingValue = info and tostring(info.value or "") or ""
                        end,
                    },
                    details = {
                        type = "description",
                        name = GetSelectedCVarSummary,
                        order = 6,
                        fontSize = "medium",
                    },
                    newValue = {
                        type = "input",
                        name = "New Value",
                        desc = "Enter the value to apply to the selected CVar.",
                        order = 7,
                        width = "full",
                        disabled = function()
                            local info = GetSelectedCVarInfo()
                            return not info or info.readOnly or info.locked
                        end,
                        get = function() return cvarPendingValue end,
                        set = function(_, value) cvarPendingValue = value or "" end,
                    },
                    apply = {
                        type = "execute",
                        name = "Apply Value",
                        desc = "Apply the new value to the selected CVar.",
                        order = 8,
                        disabled = function()
                            local info = GetSelectedCVarInfo()
                            return not info or info.readOnly or info.locked or cvarPendingValue == ""
                        end,
                        func = function()
                            if not SinsToolkit.CVars then return end
                            local ok, message = SinsToolkit.CVars:SetValue(selectedCVar, cvarPendingValue)
                            local info = GetSelectedCVarInfo()
                            cvarPendingValue = info and tostring(info.value or "") or cvarPendingValue
                            print("|cff9b59b6[Sin's Toolkit: QOL]|r " .. (message or (ok and "CVar updated." or "CVar update failed.")))
                            NotifyChange()
                        end,
                    },
                    reset = {
                        type = "execute",
                        name = "Reset to Default",
                        desc = "Set the selected CVar back to the default value reported by the WoW client.",
                        order = 9,
                        disabled = function()
                            local info = GetSelectedCVarInfo()
                            return not info or info.readOnly or info.locked
                        end,
                        func = function()
                            if not SinsToolkit.CVars then return end
                            local ok, message = SinsToolkit.CVars:ResetToDefault(selectedCVar)
                            local info = GetSelectedCVarInfo()
                            cvarPendingValue = info and tostring(info.value or "") or ""
                            print("|cff9b59b6[Sin's Toolkit: QOL]|r " .. (message or (ok and "CVar reset." or "CVar reset failed.")))
                            NotifyChange()
                        end,
                    },
                },
            },
            automation = {
                type = "group", name = GradientText("Automation"), order = 8,
                hidden = true,
                args = {
                    intro = PageIntro("Automation", "Optional helpers for repeated interaction and Druid form handling."),
                    automateGossip = { type = "toggle", name = "Automate Gossip", desc = "Automatically selects safe single-option gossip while avoiding city direction, trainer, bank, auction house, and storyteller-style gossip.", order = 1,
                        get = function() return GetToolkitValue("automateGossip") end,
                        set = function(_, v) SetToolkitValue("automateGossip", v) end,
                    },
                    automateGossipModifier = { type = "select", name = "Gossip Bypass Modifier", desc = "Hold this modifier key to temporarily bypass auto-gossip.", order = 2, values = { NONE = "None", SHIFT = "Shift", CTRL = "Ctrl", ALT = "Alt" },
                        get = function() return GetToolkitValue("automateGossipModifier") or "SHIFT" end,
                        set = function(_, v) SetToolkitValue("automateGossipModifier", v) end,
                    },
                    gseAutoUnshift = { type = "toggle", name = "Auto Unshift for Druids", desc = "Disables auto-unshift in combat for Feral/Guardian Druids. This can be helpful if you use GSE, also known as Gnome Sequencer. While enabled, Sin's Toolkit hides the SPELL_FAILED_NOT_SHAPESHIFT UI error. When unchecked, that specific error returns to Blizzard's default display behavior. Requires GSE.", order = 3,
                        disabled = function() return SinsToolkit.IsGSEAvailable and not SinsToolkit.IsGSEAvailable() end,
                        get = function() return GetToolkitValue("gseAutoUnshift") end,
                        set = function(_, v) SetToolkitValue("gseAutoUnshift", v) end,
                    },
                    druidAutoFlightForm = { type = "toggle", name = "Druid Auto Flight Form", desc = "When you are a Druid in ground Travel Form and move into an outdoor flyable area, Sin's Toolkit QOL cancels ground Travel Form so the client can move you into Flight Form on your next travel-form action. This is separate from Auto Unshift for Druids and never runs in combat, instances, challenge modes, while flying, or while submerged.", order = 4,
                        disabled = function() local _, classToken = UnitClass("player"); return classToken ~= "DRUID" end,
                        get = function() return GetToolkitValue("druidAutoFlightForm") end,
                        set = function(_, v) SetToolkitValue("druidAutoFlightForm", v) end,
                    },
                },
            },
            professions = {
                type = "group", name = GradientText("Professions & Lookups"), order = 9,
                args = {
                    intro = PageIntro("Professions & Lookups", "Hold your selected modifier over an eligible bag item to show the secure profession overlay, then click to mill, prospect, disenchant, or open supported items. Quick Link creates copyable web lookups from supported tooltips."),
                    professionItemHotkeyEnabled = { type = "toggle", name = "Profession Item Hotkey", desc = "When enabled, hold the selected modifier over an eligible bag item. Sin's Toolkit highlights supported items and routes your click through a secure profession action for milling, prospecting, disenchanting, or opening.", order = 1,
                        get = function() return GetToolkitValue("professionItemHotkeyEnabled") end,
                        set = function(_, v) SetToolkitValue("professionItemHotkeyEnabled", v) end,
                    },
                    professionItemHotkeyModifier = { type = "select", name = "Modifier Key", desc = "The modifier key to hold to show the profession item overlay.", order = 2, values = { ALT = "Alt", CTRL = "Alt + Ctrl", SHIFT = "Alt + Shift" },
                        get = function() return GetToolkitValue("professionItemHotkeyModifier") or "ALT" end,
                        set = function(_, v) SetToolkitValue("professionItemHotkeyModifier", v) end,
                    },
                    wowheadSearchEnabled = { type = "toggle", name = "Wowhead Quick Link", desc = "Mouse over supported UI elements and press Ctrl+C to open a copyable Wowhead, Armory, or Raider.IO link.", order = 3,
                        get = function() return GetToolkitValue("wowheadSearchEnabled") end,
                        set = function(_, v) SetToolkitValue("wowheadSearchEnabled", v) end,
                    },
                },
            },
            identifier = {
                type = "group", name = GradientText("Identifier"), order = 10,
                args = {
                    intro = PageIntro("Identifier", "Inspect frames under your cursor, copy frame details, and look up global frame names."),
                    enabled = { type = "toggle", name = "Enable Hover Identifier", desc = "Shows a small identifier tooltip while you move your cursor over UI frames. Use /stkid for a one-time identification without leaving hover mode enabled.", order = 1,
                        get = function() return GetToolkitValue("identifierEnabled") end,
                        set = function(_, v) SetToolkitValue("identifierEnabled", v) end,
                    },
                    tooltip = { type = "toggle", name = "Show Identifier Tooltip", desc = "Displays the current frame details near your cursor while hover identifier mode is enabled.", order = 2,
                        get = function() return GetToolkitValue("identifierTooltip") end,
                        set = function(_, v) SetToolkitValue("identifierTooltip", v) end,
                    },
                    detailed = { type = "toggle", name = "Detailed Output", desc = "Includes frame size, strata, level, alpha, point, and source-location details when the client exposes them.", order = 3,
                        get = function() return GetToolkitValue("identifierDetailed") end,
                        set = function(_, v) SetToolkitValue("identifierDetailed", v) end,
                    },
                    identifyNow = { type = "execute", name = "Identify Cursor Frame", desc = "Prints information about the frame currently under your cursor. Same as /stkid.", order = 4,
                        func = function() if SinsToolkit.Identifier then SinsToolkit.Identifier:IdentifyNow() end end,
                    },
                    copyLast = { type = "execute", name = "Copy Last Result", desc = "Opens a copyable popup for the last identified frame.", order = 5,
                        func = function() if SinsToolkit.Identifier then SinsToolkit.Identifier:CopyLast() end end,
                    },
                    commands = { type = "description", name = "|cffd8a84aCommands:|r\n|cff3498db/stkid|r - Identify the frame under your cursor.\n|cff3498db/stkid on|r / |cff3498dboff|r - Enable or disable hover mode.\n|cff3498db/stkid copy|r - Copy the last result.\n|cff3498db/stkid name <FrameName>|r - Identify a global frame by name.\n|cff3498db/stkid simple|r / |cff3498dbdetail|r - Change output detail.", fontSize = "medium", order = 6 },
                },
            },
            bugCatcher = {
                type = "group", name = GradientText("Bug Catcher"), order = 11,
                args = {
                    intro = PageIntro("Bug Catcher", "Capture Lua errors into a compact BugSack-style window with a minimap button. For testing, this can run alongside !BugGrabber. If BugSack is loaded, Sin's Toolkit disables this plugin version because BugSack owns the display window."),
                    enabled = { type = "toggle", name = "Enable Bug Catcher", desc = "Captures Lua errors, protected-action errors, and Lua warnings inside Sin's Toolkit. This can run alongside !BugGrabber for testing, but is disabled when BugSack is loaded.", order = 1,
                        get = function() return GetToolkitValue("bugCatcherEnabled") end,
                        set = function(_, v) SetToolkitValue("bugCatcherEnabled", v) end,
                    },
                    minimap = { type = "toggle", name = "Show Minimap Button", desc = "Shows the Bug Catcher minimap button. Click to show the last error, Shift-click to reload UI, Alt-click to clear errors, Right-click to hide the button.", order = 2,
                        get = function() return GetToolkitValue("bugCatcherMinimap") end,
                        set = function(_, v) SetToolkitValue("bugCatcherMinimap", v) end,
                    },
                    minimapOnlyErrors = { type = "toggle", name = "Only Show Minimap Button When Errors Exist", desc = "Keeps the Bug Catcher minimap button hidden while there are no stored errors. It appears automatically after an error is captured. Turn this off if you prefer the button to always be visible.", order = 2.5,
                        disabled = function() return not GetToolkitValue("bugCatcherMinimap") end,
                        get = function() return GetToolkitValue("bugCatcherMinimapOnlyErrors") end,
                        set = function(_, v) SetToolkitValue("bugCatcherMinimapOnlyErrors", v) end,
                    },
                    maxErrors = { type = "range", name = "Stored Error Limit", desc = "Maximum number of Lua errors Sin's Toolkit stores in its own saved variables.", order = 3, min = 25, max = 250, step = 25,
                        get = function() return GetToolkitValue("bugCatcherMaxErrors") or 100 end,
                        set = function(_, v) SetToolkitValue("bugCatcherMaxErrors", v) end,
                    },
                    sound = { type = "select", name = "Error Sound", desc = "Choose the sound Sin's Toolkit plays when Bug Catcher captures a Lua error.", order = 4, values = { NONE = "None", RAID_WARNING = "Raid Warning", ALARM = "Alarm", WHISPER = "Whisper", READY_CHECK = "Ready Check", BUGSACK_FATALITY = "BugSack: Fatality" },
                        get = function() return GetToolkitValue("bugCatcherSound") or "NONE" end,
                        set = function(_, v) SetToolkitValue("bugCatcherSound", v) end,
                    },
                    advancedHeader = { type = "header", name = "Advanced Compatibility", order = 4.5 },
                    bugSackCompatibility = { type = "toggle", name = "Enable BugSack Compatibility", desc = "Exposes BugSack-style globals so skinning addons such as WindTools can skin the Bug Catcher window. Off by default for new installs to avoid confusing addons that detect the real BugSack addon.", order = 4.6,
                        get = function() return GetToolkitValue("bugCatcherBugSackCompatibility") end,
                        set = function(_, v) SetToolkitValue("bugCatcherBugSackCompatibility", v) end,
                    },
                    open = { type = "execute", name = "Open Last Error", desc = "Opens the Bug Catcher window and displays the most recent captured error.", order = 5,
                        func = function() if SinsToolkit.BugCatcher then SinsToolkit.BugCatcher:ShowLast() end end,
                    },
                    testSound = { type = "execute", name = "Test Error Sound", desc = "Plays the currently selected Bug Catcher sound. If BugSack: Fatality is selected but BugSack media is not available, Sin's Toolkit uses a built-in Blizzard alert sound.", order = 6,
                        func = function() if SinsToolkit.BugCatcher and SinsToolkit.BugCatcher.TestSound then SinsToolkit.BugCatcher:TestSound() end end,
                    },
                    selfTest = { type = "execute", name = "Run Self-Test", desc = "Captures a safe test entry, opens Bug Catcher, and plays the configured alert sound without generating a real Lua error.", order = 6.5,
                        func = function() if SinsToolkit.BugCatcher and SinsToolkit.BugCatcher.RunSelfTest then SinsToolkit.BugCatcher:RunSelfTest() end end,
                    },
                    clear = { type = "execute", name = "Clear Errors", desc = "Clears captured errors from Sin's Toolkit.", order = 7,
                        func = function() if SinsToolkit.BugCatcher then SinsToolkit.BugCatcher:Reset() end end,
                    },
                    commands = { type = "description", name = "|cffd8a84aCommands:|r\n|cff3498db/stkbug|r - Show the last captured Lua error.\n|cff3498db/stkbug clear|r - Clear all captured errors.\n|cff3498db/stkbug copy|r - Copy the currently selected error.\n|cff3498db/stkbug sound|r - Test the selected Bug Catcher sound.\n|cff3498db/stkbug test|r - Capture a safe self-test entry.\n|cff3498db/stkbug minimap|r - Show or hide the minimap button.\n|cff3498db/stkbug sync|r - Sync captured errors from !BugGrabber for testing.\n|cff3498db/stkbug on|r / |cff3498dboff|r - Enable or disable Bug Catcher.\n|cff3498db/stkbug reload|r - Reload the UI.\n\n|cffd8a84aMinimap:|r Click shows the last error, Shift-click reloads the UI, Alt-click clears errors, and Right-click hides the minimap button. The button can also hide itself automatically while no errors are stored.", fontSize = "medium", order = 7 },
                },
            },
            teleportHub = {
                type = "group", name = GradientText("Teleport Hub"), order = 12,
                args = {
                    intro = PageIntro("Teleport Hub", "One-button access to known teleports, portals, hearthstones, and teleport toys. Favorites, Recently Used, collapsible sections, and grid/list views keep the panel compact."),
                    enabled = { type = "toggle", name = "Enable Teleport Hub", desc = "Enables Sin's Toolkit QOL Teleport Hub and the /stktp commands.", order = 1,
                        get = function() return GetToolkitValue("teleportHubEnabled") end,
                        set = function(_, v) SetToolkitValue("teleportHubEnabled", v) end,
                    },
                    minimap = { type = "toggle", name = "Show Minimap Button", desc = "Shows a small Sin's Toolkit QOL minimap button for opening the Teleport Hub. You can also use /stktp if you prefer no minimap button.", order = 2,
                        disabled = function() return not GetToolkitValue("teleportHubEnabled") end,
                        get = function() return GetToolkitValue("teleportHubMinimap") end,
                        set = function(_, v) SetToolkitValue("teleportHubMinimap", v) end,
                    },
                    showLocked = { type = "toggle", name = "Show Locked Teleports", desc = "Shows tracked teleports you have not learned or toys you do not own yet. Locked entries are dimmed and not clickable.", order = 3,
                        get = function() return GetToolkitValue("teleportHubShowLocked") end,
                        set = function(_, v) SetToolkitValue("teleportHubShowLocked", v) end,
                    },
                    showDungeonNames = { type = "toggle", name = "Show Dungeon Names", desc = "Shows dungeon or destination names when available instead of only spell names.", order = 4,
                        get = function() return GetToolkitValue("teleportHubShowDungeonNames") end,
                        set = function(_, v) SetToolkitValue("teleportHubShowDungeonNames", v) end,
                    },
                    view = { type = "select", name = "Panel View", desc = "Choose a compact icon grid or a readable list for the Teleport Hub panel. Grid uses more horizontal space; List is easier to scan when names matter.", order = 5, values = { grid = "Grid", list = "List" },
                        get = function() return GetToolkitValue("teleportHubView") or "grid" end,
                        set = function(_, v) SetToolkitValue("teleportHubView", v) end,
                    },
                    open = { type = "execute", name = "Open Teleport Hub", desc = "Opens the Teleport Hub panel. Same as /stktp.", order = 6,
                        disabled = function() return not GetToolkitValue("teleportHubEnabled") end,
                        func = function() if SinsToolkit.TeleportHub then SinsToolkit.TeleportHub:Open() end end,
                    },
                    scan = { type = "execute", name = "Scan Known Teleports", desc = "Prints how many tracked teleports are usable on this character.", order = 7,
                        func = function() if SinsToolkit.TeleportHub then SinsToolkit.TeleportHub:Scan() end end,
                    },
                    commands = { type = "description", name = "|cffd8a84aCommands:|r\n|cff3498db/stktp|r - Open or close Teleport Hub.\n|cff3498db/stktp on|r / |cff3498dboff|r - Enable or disable Teleport Hub.\n|cff3498db/stktp minimap|r - Show or hide the minimap button.\n|cff3498db/stktp scan|r - Print known teleport count.\n|cff3498db/stktp locked|r - Show or hide locked teleports.\n|cff3498db/stktp view|r - Switch between grid and list view.\n|cff3498db/stktp expand|r / |cff3498dbcollapse|r - Expand or collapse teleport sections.\n\n|cffd8a84aFavorites:|r Click the |cffffd100+ Fav|r button on any teleport to pin it near the top of the hub. Favorite entries show |cffffd100Fav|r and can be clicked again to unpin. Recently Used updates after successful clicks.", fontSize = "medium", order = 8 },
                },
            },
            profileVault = {
                type = "group", name = GradientText("Profile Vault"), order = 13,
                args = {
                    intro = PageIntro("Profile Vault", "Save and restore character setup data for action bars, macros, and keybinds."),
                    backupName = { type = "input", name = "Backup Name", desc = "Name this backup before saving your current action bars, macros, and keybinds.", order = 1, width = "full",
                        get = function() return profileVaultBackupName end,
                        set = function(_, v) profileVaultBackupName = v end,
                    },
                    createBackup = { type = "execute", name = "Create Backup", desc = "Save this character's action bars, account macros, character macros, and keybinds.", order = 2,
                        func = function()
                            if not SinsToolkit.ProfileVault then return end
                            local backup = SinsToolkit.ProfileVault:CreateBackup(profileVaultBackupName)
                            selectedProfileVaultBackup = backup.id
                            NotifyChange()
                            print("|cff9b59b6[Sin's Toolkit: QOL]|r Profile backup saved: " .. backup.name)
                        end,
                    },
                    savedBackups = { type = "select", name = "Saved Backups", desc = "Choose a backup to restore or delete.", order = 3, width = "full", values = GetProfileVaultBackupValues,
                        get = function() return selectedProfileVaultBackup end,
                        set = function(_, v) selectedProfileVaultBackup = v end,
                    },
                    summary = { type = "description", name = GetProfileVaultSummary, fontSize = "medium", order = 4 },
                    destructiveHeader = { type = "header", name = "Restore and Delete Actions", order = 4.5 },
                    restore = { type = "execute", name = "Restore to This Character", desc = "Apply the selected backup to the current character after confirmation. This can replace action bars, macros, and keybindings. Cannot be used in combat.", order = 5,
                        disabled = function() return selectedProfileVaultBackup == "" end,
                        func = function()
                            if not SinsToolkit.ProfileVault then return end
                            SinsToolkit.ProfileVault:ConfirmRestoreBackup(selectedProfileVaultBackup, function(ok, message)
                                print("|cff9b59b6[Sin's Toolkit: QOL]|r " .. (message or (ok and "Profile restored." or "Profile restore failed.")))
                            end)
                        end,
                    },
                    delete = { type = "execute", name = "Delete Backup", desc = "Delete the selected backup from Sin's Toolkit saved variables after confirmation.", order = 6,
                        disabled = function() return selectedProfileVaultBackup == "" end,
                        func = function()
                            if not SinsToolkit.ProfileVault then return end
                            SinsToolkit.ProfileVault:ConfirmDeleteBackup(selectedProfileVaultBackup, function(ok, message)
                                if ok then
                                    selectedProfileVaultBackup = ""
                                    NotifyChange()
                                end
                                print("|cff9b59b6[Sin's Toolkit: QOL]|r " .. (message or (ok and "Profile backup deleted." or "Profile backup delete failed.")))
                            end)
                        end,
                    },
                },
            },
            exports = {
                type = "group", name = GradientText("Export Strings"), order = 14,
                args = {
                    intro = PageIntro("Export Strings", "Store useful import strings and profile text in one quick reference list."),
                    savedExports = { type = "select", name = "Saved Exports", desc = "Select a saved export to view or edit.", order = 1,
                        values = function()
                            EnsureDB()
                            local list = { [""] = "None" }
                            if SinsToolkitDB.exportStrings then
                                for k in pairs(SinsToolkitDB.exportStrings) do
                                    list[k] = k
                                end
                            end
                            return list
                        end,
                        get = function() return currentExportName end,
                        set = function(_, v)
                            currentExportName = v
                            if v ~= "" then
                                currentExportString = SinsToolkitDB.exportStrings[v] or ""
                            else
                                currentExportString = ""
                            end
                        end,
                    },
                    exportName = { type = "input", name = "Export Name", desc = "Name of the export string to save or rename.", order = 2,
                        get = function() return currentExportName end,
                        set = function(_, v) currentExportName = v end,
                    },
                    exportData = { type = "input", name = "Export String", desc = "Paste your export string here.", order = 3, width = "full", multiline = 10,
                        get = function() return currentExportString end,
                        set = function(_, v) currentExportString = SanitizeExportString(v) end,
                    },
                    save = { type = "execute", name = "Save", desc = "Save the current export string.", order = 4,
                        disabled = function() return currentExportName == "" or currentExportString == "" end,
                        func = function()
                            EnsureDB()
                            SinsToolkitDB.exportStrings = SinsToolkitDB.exportStrings or {}
                            SinsToolkitDB.exportStrings[currentExportName] = currentExportString
                            NotifyChange()
                            print("|cff9b59b6[Sin's Toolkit: QOL]|r Export string saved: "..currentExportName)
                        end,
                    },
                    delete = { type = "execute", name = "Delete", desc = "Delete the selected export string.", order = 5,
                        disabled = function() return currentExportName == "" end,
                        func = function()
                            EnsureDB()
                            if SinsToolkitDB.exportStrings then
                                SinsToolkitDB.exportStrings[currentExportName] = nil
                            end
                            print("|cff9b59b6[Sin's Toolkit: QOL]|r Export string deleted: "..currentExportName)
                            currentExportName = ""
                            currentExportString = ""
                            NotifyChange()
                        end,
                    },
                    clear = { type = "execute", name = "Clear", desc = "Clear the input fields.", order = 6,
                        func = function()
                            currentExportName = ""
                            currentExportString = ""
                            NotifyChange()
                        end,
                    },
                },
            },
            advanced = {
                type = "group", name = GradientText("Advanced"), order = 15,
                hidden = true,
                args = {
                    intro = PageIntro("Advanced", "Higher-risk settings gathered in one place for review before enabling."),
                    gearHeader = { type = "header", name = "Gear Scanner", order = 1 },
                    allowUnbound = { type = "toggle", name = "Allow Unbound Upgrades", desc = "Allows Gear Scanner to equip unbound upgrades from your bags. Leave this off if you want BoE items left untouched.", order = 2,
                        get = function() return GetToolkitValue("gearScannerAllowUnbound") end,
                        set = function(_, v) SetToolkitValue("gearScannerAllowUnbound", v) end,
                    },
                    autoRollEpics = { type = "toggle", name = "Automatically Roll on Epics", desc = "Allows Gear Scanner to automatically roll on epic loot when its loot-roll rules say to handle the item.", order = 3,
                        get = function() return GetToolkitValue("gearScannerAutoRollEpics") end,
                        set = function(_, v) SetToolkitValue("gearScannerAutoRollEpics", v) end,
                    },
                    confirmEpics = { type = "toggle", name = "Confirm Epic Soulbinding", desc = "Automatically confirms soulbind prompts for epic items when Gear Scanner equips them.", order = 4,
                        get = function() return GetToolkitValue("gearScannerAutoConfirmBindEpics") end,
                        set = function(_, v) SetToolkitValue("gearScannerAutoConfirmBindEpics", v) end,
                    },
                    bugHeader = { type = "header", name = "Bug Catcher Compatibility", order = 5 },
                    bugSackCompatibility = { type = "toggle", name = "Enable BugSack Compatibility", desc = "Exposes BugSack-style globals for skinning addons such as WindTools. Off by default for new installs.", order = 6,
                        get = function() return GetToolkitValue("bugCatcherBugSackCompatibility") end,
                        set = function(_, v) SetToolkitValue("bugCatcherBugSackCompatibility", v) end,
                    },
                    bugMinimapOnlyErrors = { type = "toggle", name = "Hide Bug Catcher Minimap Button When Clean", desc = "Only shows the Bug Catcher minimap button after errors are captured. Existing users keep their previous setting; new installs use this cleaner default.", order = 6.5,
                        get = function() return GetToolkitValue("bugCatcherMinimapOnlyErrors") end,
                        set = function(_, v) SetToolkitValue("bugCatcherMinimapOnlyErrors", v) end,
                    },
                    profileHeader = { type = "header", name = "Profile Vault Safety", order = 7 },
                    profileNote = { type = "description", name = "Profile Vault restore and delete actions now require confirmation before replacing action bars, macros, or keybindings.", fontSize = "medium", order = 8 },
                },
            },
            changelog = {
                type = "group", name = GradientText("Changelog"), order = 16,
                args = {
                    intro = PageIntro("Changelog", "Recent Sin's Toolkit updates with the date and what changed."),
                    notes = { type = "description", order = 1, fontSize = "medium", name = GetChangelogText() },
                },
            },
        },
    }

    if C_Timer then
        C_Timer.After(0.1, function() UpdateErrorToggles(options.args.uiErrors.args) end)
    else
        UpdateErrorToggles(options.args.uiErrors.args)
    end

    return options
end
