local addonName, SinsToolkit = ...

if not C_AddOns.IsAddOnLoaded("ElvUI") or not _G.ElvUI then
    return
end

local E = unpack(_G.ElvUI)
local EP = E.Libs.EP
local Plugin = E:NewModule("SinsToolkit", "AceHook-3.0", "AceEvent-3.0")

local function EnsureDB()
    if SinsToolkit.EnsureDB then
        SinsToolkit.EnsureDB()
    end
end

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

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

local uiErrorListCache
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 errorSearchString = ""

local currentExportName = ""
local currentExportString = ""
local profileVaultBackupName = ""
local selectedProfileVaultBackup = ""
local cvarSearchString = ""
local cvarModifiedOnly = false
local selectedCVar = ""
local cvarPendingValue = ""
local uiErrorGroupCache = {}

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 UpdateErrorToggles()
    if not E.Options.args.sinsToolkit then return end
    local args = E.Options.args.sinsToolkit.args.uiErrors.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 = IsAnyBlocked(a)
        local bBlocked = 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()
                if E.Libs.AceConfigRegistry then E.Libs.AceConfigRegistry:NotifyChange("ElvUI") end
            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_NAME = "|cffb868dcSin's|r |cffe8b4ecToolkit|r"
local BRAND_SUBTITLE = "|cffbca7d6Compact ElvUI quality-of-life tools with a dark violet theme.|r"
local BRAND_LOGO = "Interface\\AddOns\\SinsToolkit\\Media\\Logo.tga"
local BRAND_BANNER = "Interface\\AddOns\\SinsToolkit\\Media\\Banner.tga"

local function GradientText(text)
    local startColor = { 106, 47, 153 }
    local endColor = { 255, 255, 255 }
    local out, visibleIndex = {}, 0
    local visibleCount = 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

function Plugin:InsertOptions()
    if not E.Options or not E.Options.args then return end

    E.Options.args.sinsToolkit = {
        type = "group",
        name = "|T" .. BRAND_LOGO .. ":16:16|t " .. GradientText("Sin's Toolkit"),
        desc = "Quality-of-life tools integrated as an ElvUI plugin.",
        order = 1000,
        childGroups = "tree",
        args = {
            info = {
                type = "group",
                name = GradientText("Information"),
                order = 1,
                args = {
                    brand = {
                        type = "description",
                        name = " ",
                        image = BRAND_BANNER,
                        imageWidth = 700,
                        imageHeight = 280,
                        fontSize = "medium",
                        order = 0,
                    },
                    header = {
                        type = "description",
                        name = "|cff9b59b6Sin's Toolkit Feature Overview|r\n\nWelcome to Sin's Toolkit! This is a fan-made tweak collection built as a personal quality-of-life plugin for ElvUI. It was created to bring multiple small addon-style tools into one location, and it will continue to be updated and expanded as World of Warcraft changes through future expansions and game updates.\n\n- |cffd8a84aGear Scanner:|r Automatically equips gear upgrades as you loot them based on your spec's stat priorities, with optional loot-roll, soulbind confirmation, tooltip, locked-slot, and Pawn support. Inspired by AutoGear, with ClassCodex used for stat priority data.\n\n- |cffd8a84aUI Tweaks:|r Filters annoying red error text from your screen and lets multiple Blizzard panels stay open together. No Auto Close behavior was inspired by NoAutoClose.\n\n- |cffd8a84aCVars:|r Browse live client CVars, compare current values against Blizzard defaults, edit selected values, and filter to modified-only settings. Inspired by AdvancedInterfaceOptions. Modified CVars appear |cff55ff55green|r in the list.\n\n- |cffd8a84aAutomation:|r Speeds up interactions by auto-selecting gossip options and automates Auto Unshift for Druids in combat. This can be helpful if you use GSE, also known as Gnome Sequencer. While enabled, it also hides the shapeshift-form error from your screen; turning it off restores Blizzard's default error display. Auto Unshift for Druids was inspired by EA Auto Unshift.\n\n- |cffd8a84aProfessions & Lookups:|r Adds a secure hotkey overlay to profession items in your bags and creates copyable Wowhead links from supported tooltips. Profession item behavior was inspired by Molinari, and tooltip link behavior was inspired by Wowhead Quick Link.\n\n- |cffd8a84aProfile Vault:|r Saves and restores action bars, macros, and keybind backups. Inspired by MySlot-style profile backups.\n\n- |cffd8a84aExport Strings:|r Save, manage, and retrieve text strings like UI layouts, WeakAuras, or talent builds. This section was created for Sin's Toolkit.\n\n|cffd8a84aAvailable Commands:|r\n- |cff3498db/stk status|r - Prints the active status of your toolkit features to chat.\n- |cff3498db/stk scan|r - Forces a manual gear scan for upgrades.\n- |cff3498db/sau status|r - Checks the status of Auto Unshift for Druids.",
                        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 ClassCodex stat priorities."),
                    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 ClassCodex priorities or Pawn when Pawn support is enabled.",
                        order = 1,
                        disabled = function() return SinsToolkit.IsClassCodexAvailable and not SinsToolkit.IsClassCodexAvailable() end,
                        get = function() return GetToolkitValue("gearScannerEnabled") end,
                        set = function(_, value) SetToolkitValue("gearScannerEnabled", value) 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(_, value) SetToolkitValue("gearScannerMaxLevelDisable", value) 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(_, value) SetToolkitValue("gearScannerSilent", value) 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 = "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(_, value) SetToolkitValue("gearScannerScanOnLoot", value) 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(_, value) SetToolkitValue("gearScannerAllowUnbound", value) 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(_, value) SetToolkitValue("gearScannerMinimumUpgrade", value) end,
                    },
                    pawnHeader = {
                        type = "header",
                        name = "Pawn Support",
                        order = 9,
                    },
                    usePawn = {
                        type = "toggle",
                        name = "Use Pawn to Evaluate Upgrades",
                        desc = "Uses Pawn's configured class/spec scales to decide upgrades when Pawn is installed and loaded. To use this, install Pawn, set up or import the stat weights you want for your class/spec in Pawn, then enable this option. If Pawn is missing or cannot score an item, Sin's Toolkit falls back to ClassCodex priorities.",
                        order = 9.1,
                        disabled = function() return SinsToolkit.IsPawnAvailable and not SinsToolkit.IsPawnAvailable() end,
                        get = function() return GetToolkitValue("gearScannerUsePawn") end,
                        set = function(_, value) SetToolkitValue("gearScannerUsePawn", value) 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(_, value) SetToolkitValue("gearScannerAutoRollGreensBopBlues", value) 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(_, value) SetToolkitValue("gearScannerAutoRollBoeBlues", value) 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(_, value) SetToolkitValue("gearScannerAutoRollEpics", value) 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(_, value) SetToolkitValue("gearScannerRollNonGearLoot", value) 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(_, value) SetToolkitValue("gearScannerNeverAutoNeed", value) 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(_, value) SetToolkitValue("gearScannerAutoConfirmBindOther", value) 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(_, value) SetToolkitValue("gearScannerAutoConfirmBindBlues", value) 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(_, value) SetToolkitValue("gearScannerAutoConfirmBindEpics", value) 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. Scores use Pawn when Pawn support is enabled and available; otherwise they use ClassCodex stat priorities.",
                        order = 11,
                        get = function() return GetToolkitValue("gearScannerScoreTooltips") end,
                        set = function(_, value) SetToolkitValue("gearScannerScoreTooltips", value) 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, missing Pawn support, or being an upgrade candidate.",
                        order = 12,
                        get = function() return GetToolkitValue("gearScannerReasonsTooltips") end,
                        set = function(_, value) SetToolkitValue("gearScannerReasonsTooltips", value) 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(_, value) SetToolkitValue("gearScannerLockSlots", value) 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"),
                order = 5,
                args = {
                    intro = PageIntro("UI Tweaks", "Small interface behavior changes that keep everyday panels and messages cleaner."),
                    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(_, value) SetToolkitValue("keepPanelsOpen", value) 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(_, value) SetToolkitValue("hideOutOfRangeErrors", value) 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()
                            if E.Libs.AceConfigRegistry then E.Libs.AceConfigRegistry:NotifyChange("ElvUI") end
                        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 ""
                            if E.Libs.AceConfigRegistry then E.Libs.AceConfigRegistry:NotifyChange("ElvUI") end
                        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
                            if E.Libs.AceConfigRegistry then E.Libs.AceConfigRegistry:NotifyChange("ElvUI") end
                        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
                            if E.Libs.AceConfigRegistry then E.Libs.AceConfigRegistry:NotifyChange("ElvUI") end
                        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]|r " .. (message or (ok and "CVar updated." or "CVar update failed.")))
                            if E.Libs.AceConfigRegistry then E.Libs.AceConfigRegistry:NotifyChange("ElvUI") end
                        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]|r " .. (message or (ok and "CVar reset." or "CVar reset failed.")))
                            if E.Libs.AceConfigRegistry then E.Libs.AceConfigRegistry:NotifyChange("ElvUI") end
                        end,
                    },
                },
            },
            automation = {
                type = "group",
                name = GradientText("Automation"),
                order = 8,
                args = {
                    intro = PageIntro("Automation", "Optional helpers for repeated interaction and Druid form handling."),
                    automateGossip = {
                        type = "toggle",
                        name = "Automate Gossip",
                        desc = "Automatically selects the first available gossip option.",
                        order = 1,
                        get = function() return GetToolkitValue("automateGossip") end,
                        set = function(_, value) SetToolkitValue("automateGossip", value) 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(_, value) SetToolkitValue("automateGossipModifier", value) 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(_, value) SetToolkitValue("gseAutoUnshift", value) end,
                    },
                },
            },
            professions = {
                type = "group",
                name = GradientText("Professions & Lookups"),
                order = 9,
                args = {
                    intro = PageIntro("Professions & Lookups", "Modifier tools for profession items and quick copyable web lookups."),
                    professionItemHotkeyEnabled = {
                        type = "toggle",
                        name = "Profession Item Hotkey",
                        desc = "Shows a secure modifier-click overlay on eligible profession items.",
                        order = 1,
                        get = function() return GetToolkitValue("professionItemHotkeyEnabled") end,
                        set = function(_, value) SetToolkitValue("professionItemHotkeyEnabled", value) 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(_, value) SetToolkitValue("professionItemHotkeyModifier", value) 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(_, value) SetToolkitValue("wowheadSearchEnabled", value) end,
                    },
                },
            },
            profileVault = {
                type = "group",
                name = GradientText("Profile Vault"),
                order = 10,
                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(_, value) profileVaultBackupName = value 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
                            if E.Libs.AceConfigRegistry then E.Libs.AceConfigRegistry:NotifyChange("ElvUI") end
                            print("|cff9b59b6[Sin's Toolkit]|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(_, value) selectedProfileVaultBackup = value end,
                    },
                    summary = {
                        type = "description",
                        name = GetProfileVaultSummary,
                        fontSize = "medium",
                        order = 4,
                    },
                    restore = {
                        type = "execute",
                        name = "Restore to This Character",
                        desc = "Apply the selected backup to the current character. Cannot be used in combat.",
                        order = 5,
                        disabled = function() return selectedProfileVaultBackup == "" end,
                        func = function()
                            if not SinsToolkit.ProfileVault then return end
                            local ok, message = SinsToolkit.ProfileVault:RestoreBackup(selectedProfileVaultBackup)
                            print("|cff9b59b6[Sin's Toolkit]|r " .. (message or (ok and "Profile restored." or "Profile restore failed.")))
                        end,
                    },
                    delete = {
                        type = "execute",
                        name = "Delete Backup",
                        desc = "Delete the selected backup from Sin's Toolkit saved variables.",
                        order = 6,
                        disabled = function() return selectedProfileVaultBackup == "" end,
                        func = function()
                            if not SinsToolkit.ProfileVault then return end
                            SinsToolkit.ProfileVault:DeleteBackup(selectedProfileVaultBackup)
                            selectedProfileVaultBackup = ""
                            if E.Libs.AceConfigRegistry then E.Libs.AceConfigRegistry:NotifyChange("ElvUI") end
                            print("|cff9b59b6[Sin's Toolkit]|r Profile backup deleted.")
                        end,
                    },
                },
            },
            exports = {
                type = "group",
                name = GradientText("Export Strings"),
                order = 11,
                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(_, value)
                            currentExportName = value
                            if value ~= "" then
                                currentExportString = SinsToolkitDB.exportStrings[value] 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(_, value) currentExportName = value 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(_, value) currentExportString = SanitizeExportString(value) 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
                            if E.Libs.AceConfigRegistry then E.Libs.AceConfigRegistry:NotifyChange("ElvUI") end
                            print("|cff9b59b6[Sin's Toolkit]|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]|r Export string deleted: "..currentExportName)
                            currentExportName = ""
                            currentExportString = ""
                            if E.Libs.AceConfigRegistry then E.Libs.AceConfigRegistry:NotifyChange("ElvUI") end
                        end,
                    },
                    clear = {
                        type = "execute",
                        name = "Clear",
                        desc = "Clear the input fields.",
                        order = 6,
                        func = function()
                            currentExportName = ""
                            currentExportString = ""
                            if E.Libs.AceConfigRegistry then E.Libs.AceConfigRegistry:NotifyChange("ElvUI") end
                        end,
                    },
                },
            },
            changelog = {
                type = "group",
                name = GradientText("Changelog"),
                order = 12,
                args = {
                    intro = PageIntro("Changelog", "Recent Sin's Toolkit updates with the date and what changed."),
                    notes = {
                        type = "description",
                        order = 1,
                        fontSize = "medium",
                        name = "|cffd8a84aVersion 1.1.17 - June 7, 2026|r\n"
                            .. "- Updated the Information page banner using the cleaner Sin's Toolkit banner asset.\n"
                            .. "- Fixed the banner display ratio so it no longer appears cramped or stretched at the top.\n"
                            .. "- Optimized UI Errors tab loading by caching grouped error-message results.\n"
                            .. "- Optimized the CVar selector by caching dropdown values per search and modified-only filter.\n\n"
                            .. "|cffd8a84aVersion 1.1.16 - June 7, 2026|r\n"
                            .. "- Fixed UI Error Filtering so duplicate Blizzard error IDs with the same displayed text are blocked together.\n"
                            .. "- Cleaned up the UI Errors list so duplicate displayed messages behave as one checkbox.\n"
                            .. "- Verified Auto Unshift for Druids still only suppresses its shapeshift-form error while that feature is enabled.\n\n"
                            .. "|cffd8a84aVersion 1.1.15 - June 7, 2026|r\n"
                            .. "- Fixed Gear Scanner scan filtering so valid gear is not rejected by an overly broad usable-item check.\n"
                            .. "- Changed the ClassCodex dependency guard so Gear Scanner pauses when ClassCodex is unavailable instead of silently turning off the saved toggle.\n\n"
                            .. "|cffd8a84aVersion 1.1.14 - June 7, 2026|r\n"
                            .. "- Adjusted Gear Scanner scoring for leveling so item level and item quality are considered more clearly.\n"
                            .. "- ClassCodex stat priorities remain the primary comparison, with rarity and item level used as supporting score factors.\n\n"
                            .. "|cffd8a84aVersion 1.1.13 - June 7, 2026|r\n"
                            .. "- Fixed Gear Scanner armor filtering so it will not equip the wrong armor type for your class.\n"
                            .. "- Added clearer tooltip feedback when an item is rejected because it is not your class armor type.\n\n"
                            .. "|cffd8a84aVersion 1.1.12 - June 7, 2026|r\n"
                            .. "- Added this in-game Changelog page after Export Strings.\n\n"
                            .. "|cffd8a84aVersion 1.1.11 - June 7, 2026|r\n"
                            .. "- Updated Auto Unshift for Druids tooltip wording.\n"
                            .. "- Updated the Information panel description for Auto Unshift for Druids.\n"
                            .. "- Added clarification that Auto Unshift can be helpful for GSE, also known as Gnome Sequencer.\n"
                            .. "- Added suppression for the shapeshift-form UI error while Auto Unshift for Druids is enabled.\n"
                            .. "- Restored Blizzard's default shapeshift error behavior when Auto Unshift for Druids is disabled.\n\n"
                            .. "|cffd8a84aVersion 1.1.10 - June 7, 2026|r\n"
                            .. "- Expanded Gear Scanner tooltips.\n"
                            .. "- Added clearer Pawn setup instructions.\n"
                            .. "- Improved loot-roll and soulbind confirmation option descriptions.\n"
                            .. "- Fixed the sidebar gradient so it flows from dark purple to white.\n\n"
                            .. "|cffd8a84aVersion 1.1.9 - June 7, 2026|r\n"
                            .. "- Added optional Gear Scanner loot-roll settings.\n"
                            .. "- Added optional soulbind confirmation settings.\n"
                            .. "- Added optional Pawn support and Pawn as an optional dependency.\n"
                            .. "- Added support for showing won't-equip reasons in item tooltips.\n"
                            .. "- Updated the addon banner image.\n\n"
                            .. "|cffd8a84aVersion 1.1.8 - June 7, 2026|r\n"
                            .. "- Added custom Sin's Toolkit banner artwork.\n"
                            .. "- Updated the ElvUI options panel branding.\n"
                            .. "- Added purple and violet visual styling to the options layout.\n\n"
                            .. "|cffd8a84aVersion 1.1.7 - June 7, 2026|r\n"
                            .. "- Redesigned the options layout inside ElvUI.\n"
                            .. "- Changed the options panel from top tabs to left-side navigation.\n"
                            .. "- Added compact section introductions.\n"
                            .. "- Added a branded Sin's Toolkit information area.",
                    },
                },
            },
        },
    }

    UpdateErrorToggles()
end

function Plugin:Initialize()
end

local function OptionsCallback()
    Plugin:InsertOptions()
end

if EP then
    EP:RegisterPlugin(addonName, OptionsCallback)
end

E:RegisterModule(Plugin:GetName())

