local addonName, ns = ...
local L = ns.L or {}
local ContainerItems = ns.ContainerItems or {}

local ASSET_ROOT = "Interface\\AddOns\\" .. tostring(addonName or "Vaultloom") .. "\\Assets\\"
local ONE_CLICK_BUTTON_TEXTURE = ASSET_ROOT .. "class_backplate_reskin.tga"
local ONE_CLICK_RING_TEXTURE = ASSET_ROOT .. "dock_ring_clean.tga"

local function registerOneClickProcessingFeature()
    local PROCESSING_DATA = {
        millable = {
            [210796] = { 444181, 10 }, -- Mycobloom
            [210797] = { 444181, 10 },
            [210798] = { 444181, 10 },
            [210799] = { 444181, 10 }, -- Luredrop
            [210800] = { 444181, 10 },
            [210801] = { 444181, 10 },
            [210802] = { 444181, 10 }, -- Orbinid
            [210803] = { 444181, 10 },
            [210804] = { 444181, 10 },
            [210805] = { 444181, 10 }, -- Blessing Blossom
            [210806] = { 444181, 10 },
            [210807] = { 444181, 10 },
            [220141] = { 444181, 10 }, -- Specular Rainbowfish
            [236761] = { 1269575, 10 }, -- Tranquility Bloom
            [236767] = { 1269575, 10 },
            [236770] = { 1269575, 10 }, -- Sanguithorn
            [236771] = { 1269575, 10 },
            [236776] = { 1269575, 10 }, -- Argentleaf
            [236777] = { 1269575, 10 },
            [236778] = { 1269575, 10 }, -- Mana Lily
            [236779] = { 1269575, 10 },
        },
        prospectable = {
            [210930] = { 434018, 5 }, -- Bismuth
            [210931] = { 434018, 5 },
            [210932] = { 434018, 5 },
            [210933] = { 434018, 5 }, -- Aqirite
            [210934] = { 434018, 5 },
            [210935] = { 434018, 5 },
            [210936] = { 434018, 5 }, -- Ironclaw Ore
            [210937] = { 434018, 5 },
            [210938] = { 434018, 5 },
            [210939] = { 434018, 5 }, -- Null Stone
            [213398] = { 434018, 5 }, -- Handful of Pebbles
            [237359] = { 1231127, 5 }, -- Refulgent Copper Ore
            [237361] = { 1231127, 5 },
            [237362] = { 1231127, 5 }, -- Umbral Tin Ore
            [237363] = { 1231127, 5 },
            [237364] = { 1231127, 5 }, -- Brilliant Silver Ore
            [237365] = { 1231127, 5 },
            [237366] = { 1231127, 5 }, -- Dazzling Thorium
            [240216] = { 434018, 5 }, -- K'areshi Resonating Stone
            [249218] = { 434018, 5 }, -- Manaforged Instrument
        },
        openable = {
            [4632] = 15,
            [4633] = 15,
            [4634] = 15,
            [4636] = 15,
            [4637] = 15,
            [4638] = 15,
            [5758] = 15,
            [5759] = 15,
            [5760] = 15,
            [6354] = 15,
            [6355] = 15,
            [7209] = 1,
            [12033] = 15,
            [13875] = 15,
            [13918] = 15,
            [16882] = 15,
            [16883] = 15,
            [16884] = 15,
            [16885] = 15,
            [29569] = 30,
            [31952] = 30,
            [43575] = 30,
            [43622] = 30,
            [43624] = 30,
            [45986] = 30,
            [63349] = 30,
            [68729] = 30,
            [88165] = 35,
            [88567] = 35,
            [106895] = 40,
            [116920] = 40,
            [121331] = 45,
            [169475] = 50,
            [179311] = 60,
            [180522] = 60,
            [180532] = 60,
            [180533] = 60,
            [186160] = 60,
            [186161] = 60,
            [188787] = 60,
            [190954] = 65,
            [198657] = 65,
            [203743] = 15,
            [204307] = 15,
            [220376] = 75,
            [264475] = 85,
        },
        keys = {
            [4367] = { 15, 0 },
            [4398] = { 20, 0 },
            [7208] = { 1, 0 },
            [15869] = { 15, 0 },
            [15870] = { 15, 0 },
            [15871] = { 20, 0 },
            [15872] = { 30, 0 },
            [18594] = { 30, 0 },
            [23819] = { 30, 0 },
            [43853] = { 30, 0 },
            [43854] = { 30, 0 },
            [55053] = { 35, 0 },
            [60853] = { 35, 0 },
            [77532] = { 35, 0 },
            [82960] = { 35, 0 },
            [159825] = { 50, 0 },
            [159826] = { 50, 0 },
            [171441] = { 60, 51 },
            [173065] = { 60, 51 },
            [191256] = { 70, 61 },
            [222523] = { 80, 1 },
            [260232] = { 90, 1 },
        },
    }

    local COLORS = {
        mill = { 0.18, 0.86, 0.42, 0.95 },
        prospect = { 0.28, 0.74, 1.00, 0.95 },
        disenchant = { 0.66, 0.48, 1.00, 0.95 },
        open = { 1.00, 0.78, 0.26, 0.95 },
    }

    local MACRO_SALVAGE = "/run C_TradeSkillUI.CraftSalvage(%d,1,ItemLocation:CreateFromBagAndSlot(%d,%d))"

    local Runtime = {
        enabled = false,
        hooksReady = false,
        hidePending = false,
    }

    local function isCombatLocked()
        return type(InCombatLockdown) == "function" and InCombatLockdown()
    end

    local function isPlayerBag(bagID)
        bagID = tonumber(bagID)
        return bagID ~= nil and bagID >= 0 and bagID <= 5
    end

    local function getItemIDFromLink(itemLink)
        if type(itemLink) ~= "string" then
            return nil
        end
        return tonumber(itemLink:match("item:(%d+)"))
    end

    local function isSpellKnown(spellID)
        spellID = tonumber(spellID)
        if not spellID then
            return false
        end

        local checker = C_SpellBook and (C_SpellBook.IsSpellKnown or C_SpellBook.IsSpellKnownOrOverridesKnown)
        if type(checker) == "function" then
            local ok, known = pcall(checker, spellID)
            if ok and known then
                return true
            end
        end

        if type(IsPlayerSpell) == "function" then
            local ok, known = pcall(IsPlayerSpell, spellID)
            return ok and known == true
        end

        return false
    end

    local function getSpellName(spellID)
        if C_Spell and type(C_Spell.GetSpellName) == "function" then
            local ok, name = pcall(C_Spell.GetSpellName, spellID)
            if ok and type(name) == "string" and name ~= "" then
                return name
            end
        end
        if type(GetSpellInfo) == "function" then
            local ok, name = pcall(GetSpellInfo, spellID)
            if ok and type(name) == "string" and name ~= "" then
                return name
            end
        end
        return tostring(spellID)
    end

    local function getSpellIcon(spellID)
        if C_Spell and type(C_Spell.GetSpellTexture) == "function" then
            local ok, texture = pcall(C_Spell.GetSpellTexture, spellID)
            if ok and texture then
                return texture
            end
        end
        if type(GetSpellTexture) == "function" then
            local ok, texture = pcall(GetSpellTexture, spellID)
            if ok and texture then
                return texture
            end
        end
        return 134400
    end

    local function getItemIcon(itemID)
        if C_Item and type(C_Item.GetItemIconByID) == "function" then
            local ok, texture = pcall(C_Item.GetItemIconByID, itemID)
            if ok and texture then
                return texture
            end
        end
        if type(GetItemIcon) == "function" then
            local ok, texture = pcall(GetItemIcon, itemID)
            if ok and texture then
                return texture
            end
        end
        return 134400
    end

    local function getStackCount(bagID, slotID)
        if C_Container and type(C_Container.GetContainerItemInfo) == "function" then
            local ok, info = pcall(C_Container.GetContainerItemInfo, bagID, slotID)
            if ok and type(info) == "table" then
                return tonumber(info.stackCount or info.quantity) or 0
            end
        end
        if type(GetContainerItemInfo) == "function" then
            local ok, _, count = pcall(GetContainerItemInfo, bagID, slotID)
            if ok then
                return tonumber(count) or 0
            end
        end
        return 0
    end

    local function getItemInfo(itemLink, itemID)
        local quality, classID, subClassID, inventoryType
        if C_Item and type(C_Item.GetItemInfoInstant) == "function" then
            local _, _, _, equipLoc, _, resolvedClassID, resolvedSubClassID = C_Item.GetItemInfoInstant(itemLink or itemID)
            classID = resolvedClassID
            subClassID = resolvedSubClassID
            inventoryType = equipLoc
        end
        if type(GetItemInfo) == "function" then
            local _, _, resolvedQuality, _, _, _, _, _, equipLoc, _, _, resolvedClassID, resolvedSubClassID = GetItemInfo(itemLink or itemID)
            quality = tonumber(resolvedQuality)
            classID = classID or resolvedClassID
            subClassID = subClassID or resolvedSubClassID
            inventoryType = inventoryType or equipLoc
        end
        return quality, classID, subClassID, inventoryType
    end

    local function getModifier()
        return (ns.GetFeatureOptionValue and ns:GetFeatureOptionValue("one_click_processing", "modifier")) or "alt"
    end

    local function getModifierLabel()
        local modifier = getModifier()
        if modifier == "alt_shift" then
            return L.FEATURE_ONE_CLICK_PROCESSING_MODIFIER_ALT_SHIFT or "Alt + Shift"
        elseif modifier == "alt_ctrl" then
            return L.FEATURE_ONE_CLICK_PROCESSING_MODIFIER_ALT_CTRL or "Alt + Ctrl"
        end
        return L.FEATURE_ONE_CLICK_PROCESSING_MODIFIER_ALT or "Alt"
    end

    local function isModifierActive()
        local modifier = getModifier()
        if not IsAltKeyDown() then
            return false
        end
        if modifier == "alt_shift" then
            return IsShiftKeyDown() and not IsControlKeyDown()
        elseif modifier == "alt_ctrl" then
            return IsControlKeyDown() and not IsShiftKeyDown()
        end
        return not IsShiftKeyDown() and not IsControlKeyDown()
    end

    local function makeSpellAction(kind, spellID, requiredCount, color)
        if not isSpellKnown(spellID) then
            return nil
        end
        return {
            kind = kind,
            spellID = spellID,
            requiredCount = requiredCount,
            label = getSpellName(spellID),
            icon = getSpellIcon(spellID),
            color = color,
        }
    end

    local function findKeyForLockbox(requiredLevel)
        requiredLevel = tonumber(requiredLevel)
        if not requiredLevel then
            return nil
        end

        local playerLevel = UnitLevel("player") or 0
        for itemID, info in pairs(PROCESSING_DATA.keys) do
            local keyPower = tonumber(info and info[1]) or 0
            local minLevel = tonumber(info and info[2]) or 0
            if keyPower >= requiredLevel and minLevel <= playerLevel and C_Item and type(C_Item.GetItemCount) == "function" then
                local ok, count = pcall(C_Item.GetItemCount, itemID)
                if ok and (tonumber(count) or 0) > 0 then
                    return itemID
                end
            end
        end

        return nil
    end

    local function getProcessingAction(itemID, itemLink, bagID, slotID)
        if not itemID or not isPlayerBag(bagID) or not slotID then
            return nil
        end

        local millInfo = PROCESSING_DATA.millable[itemID]
        if millInfo and getStackCount(bagID, slotID) >= (tonumber(millInfo[2]) or 1) then
            local action = makeSpellAction("mill", millInfo[1], millInfo[2], COLORS.mill)
            if action then
                return action
            end
        end

        local prospectInfo = PROCESSING_DATA.prospectable[itemID]
        if prospectInfo and getStackCount(bagID, slotID) >= (tonumber(prospectInfo[2]) or 1) then
            local action = makeSpellAction("prospect", prospectInfo[1], prospectInfo[2], COLORS.prospect)
            if action then
                return action
            end
        end

        local requiredLevel = PROCESSING_DATA.openable[itemID]
        if requiredLevel then
            if isSpellKnown(1804) and tonumber(requiredLevel) <= (UnitLevel("player") or 0) then
                return makeSpellAction("open", 1804, nil, COLORS.open)
            elseif isSpellKnown(312890) and tonumber(requiredLevel) <= (UnitLevel("player") or 0) then
                return makeSpellAction("open", 312890, nil, COLORS.open)
            elseif isSpellKnown(323427) and tonumber(requiredLevel) <= 60 then
                return makeSpellAction("open", 323427, nil, COLORS.open)
            end

            local keyItemID = findKeyForLockbox(requiredLevel)
            if keyItemID then
                return {
                    kind = "open",
                    itemID = keyItemID,
                    label = (C_Item and C_Item.GetItemNameByID and C_Item.GetItemNameByID(keyItemID)) or tostring(keyItemID),
                    icon = getItemIcon(keyItemID),
                    color = COLORS.open,
                }
            end
        end

        if not isSpellKnown(13262) then
            return nil
        end

        if C_Item and type(C_Item.IsCosmeticItem) == "function" then
            local okCosmetic, isCosmetic = pcall(C_Item.IsCosmeticItem, itemID)
            if okCosmetic and isCosmetic then
                return nil
            end
        end

        local quality, classID, subClassID, inventoryType = getItemInfo(itemLink, itemID)
        local uncommonQuality = Enum and Enum.ItemQuality and Enum.ItemQuality.Uncommon or 2
        local epicQuality = Enum and Enum.ItemQuality and Enum.ItemQuality.Epic or 4
        local weaponClass = Enum and Enum.ItemClass and Enum.ItemClass.Weapon or 2
        local armorClass = Enum and Enum.ItemClass and Enum.ItemClass.Armor or 4
        local professionClass = Enum and Enum.ItemClass and Enum.ItemClass.Profession or 19
        local gemClass = Enum and Enum.ItemClass and Enum.ItemClass.Gem or 3
        local artifactRelicSubclass = Enum and Enum.ItemGemSubclass and Enum.ItemGemSubclass.Artifactrelic or -1

        if not quality or quality < uncommonQuality or quality > epicQuality then
            return nil
        end
        if classID ~= weaponClass and classID ~= armorClass and classID ~= professionClass and not (classID == gemClass and subClassID == artifactRelicSubclass) then
            return nil
        end
        if inventoryType == "INVTYPE_BODY" then
            return nil
        end

        return makeSpellAction("disenchant", 13262, nil, COLORS.disenchant)
    end

    local function setButtonActionType(button, actionType)
        button:SetAttribute("type1", actionType)
        button:SetAttribute("alt-type1", actionType)
        button:SetAttribute("alt-shift-type1", actionType)
        button:SetAttribute("alt-ctrl-type1", actionType)
    end

    function Runtime:EnsureButton()
        if self.button then
            return self.button
        end

        local button = CreateFrame("Button", "VaultloomOneClickProcessingButton", UIParent, "SecureActionButtonTemplate,SecureHandlerEnterLeaveTemplate")
        button:SetFrameStrata("TOOLTIP")
        button:SetSize(38, 38)
        button:RegisterForClicks("LeftButtonUp")
        button:EnableMouse(true)
        button:Hide()

        button.tint = button:CreateTexture(nil, "BACKGROUND")
        button.tint:SetAllPoints()
        button.tint:SetColorTexture(0, 0, 0, 0.08)

        button.badgeBack = button:CreateTexture(nil, "ARTWORK")
        button.badgeBack:SetSize(18, 18)
        button.badgeBack:SetPoint("BOTTOMRIGHT", -3, 3)
        button.badgeBack:SetTexture(ONE_CLICK_BUTTON_TEXTURE)
        button.badgeBack:SetVertexColor(0.06, 0.06, 0.07, 0.94)

        button.badgeRing = button:CreateTexture(nil, "OVERLAY")
        button.badgeRing:SetAllPoints(button.badgeBack)
        button.badgeRing:SetTexture(ONE_CLICK_RING_TEXTURE)
        button.badgeRing:SetVertexColor(0.76, 0.64, 0.24, 0.85)

        button.actionIcon = button:CreateTexture(nil, "OVERLAY")
        button.actionIcon:SetSize(13, 13)
        button.actionIcon:SetPoint("CENTER", button.badgeBack, "CENTER", 0, 0)
        button.actionIcon:SetTexCoord(0.08, 0.92, 0.08, 0.92)

        button:SetScript("OnShow", function(selfButton)
            Runtime:AnchorButton()
        end)
        button:SetScript("OnEnter", function(selfButton)
            Runtime:ShowButtonTooltip(selfButton)
        end)
        button:SetScript("OnLeave", function()
            GameTooltip:Hide()
            Runtime:HideButton()
        end)
        button:SetAttribute("_onleave", "self:Hide()")

        self.button = button
        return button
    end

    function Runtime:AnchorButton()
        local button = self.button
        local owner = self.owner or (GameTooltip and GameTooltip:GetOwner()) or nil
        if not button or not owner or not owner.GetScaledRect then
            return
        end

        local ok, left, bottom, width, height = pcall(owner.GetScaledRect, owner)
        if not ok or not left or not bottom or not width or not height then
            return
        end

        local scaleMultiplier = 1 / UIParent:GetScale()
        button:ClearAllPoints()
        button:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left * scaleMultiplier, bottom * scaleMultiplier)
        button:SetSize(width * scaleMultiplier, height * scaleMultiplier)
    end

    function Runtime:HideButton()
        local button = self.button
        self.owner = nil
        self.action = nil
        self.itemLink = nil
        self.bagID = nil
        self.slotID = nil
        if button then
            if isCombatLocked() then
                self.hidePending = true
                if self.eventFrame then
                    self.eventFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
                end
                return
            end

            self.hidePending = false
            button:Hide()
            button:SetAttribute("target-bag", nil)
            button:SetAttribute("target-slot", nil)
            button:SetAttribute("spell", nil)
            button:SetAttribute("item", nil)
            button:SetAttribute("macrotext", nil)
        end
    end

    function Runtime:StyleButton(action)
        local button = self.button
        if not button then
            return
        end

        local color = action and action.color or COLORS.open
        button.tint:SetColorTexture(color[1], color[2], color[3], 0.08)
        button.actionIcon:SetTexture(action and action.icon or 134400)
        button.actionIcon:SetVertexColor(0.96, 0.96, 0.96, 1)
        button.badgeRing:SetVertexColor(color[1], color[2], color[3], 0.90)
    end

    function Runtime:ShowButtonTooltip(button)
        if not button then
            return
        end

        GameTooltip:SetOwner(button, "ANCHOR_RIGHT")
        if self.bagID and self.slotID then
            GameTooltip:SetBagItem(self.bagID, self.slotID)
        elseif self.itemLink then
            GameTooltip:SetHyperlink(self.itemLink)
        end

        local action = self.action
        if action then
            GameTooltip:AddLine(" ")
            GameTooltip:AddLine(
                string.format(
                    L.FEATURE_ONE_CLICK_PROCESSING_TOOLTIP_USE or "%s + left click: %s",
                    getModifierLabel(),
                    action.label or ""
                ),
                action.color[1],
                action.color[2],
                action.color[3],
                true
            )
        end
        GameTooltip:Show()
    end

    function Runtime:ApplyAttributes(action, bagID, slotID)
        local button = self:EnsureButton()
        if isCombatLocked() then
            return false
        end

        button:SetAttribute("target-bag", bagID)
        button:SetAttribute("target-slot", slotID)
        button:SetAttribute("spell", nil)
        button:SetAttribute("item", nil)
        button:SetAttribute("macrotext", nil)

        if action.itemID then
            button:SetAttribute("item", "item:" .. tostring(action.itemID))
            setButtonActionType(button, "item")
        elseif action.spellID then
            local useSalvageMacro = C_TradeSkillUI and type(C_TradeSkillUI.CraftSalvage) == "function"
                and ItemLocation
                and type(ItemLocation.CreateFromBagAndSlot) == "function"
                and (type(FindSpellBookSlotBySpellID) ~= "function" or not FindSpellBookSlotBySpellID(action.spellID))
            if useSalvageMacro then
                button:SetAttribute("macrotext", string.format(MACRO_SALVAGE, action.spellID, bagID, slotID))
                setButtonActionType(button, "macro")
            else
                button:SetAttribute("spell", action.spellID)
                setButtonActionType(button, "spell")
            end
        else
            return false
        end

        return true
    end

    function Runtime:GetTooltipContext(tooltip, data)
        local owner = tooltip and tooltip:GetOwner() or nil
        local bagID, slotID = ContainerItems:GetButtonBagAndSlot(owner)
        local itemLink = bagID and slotID and ContainerItems:GetItemLink(bagID, slotID) or nil

        if (bagID == nil or slotID == nil) and type(data) == "table" and data.guid and C_Item and type(C_Item.GetItemLocation) == "function" then
            local okLocation, itemLocation = pcall(C_Item.GetItemLocation, data.guid)
            if okLocation and itemLocation and itemLocation.IsBagAndSlot and itemLocation:IsBagAndSlot() then
                bagID, slotID = itemLocation:GetBagAndSlot()
                itemLink = ContainerItems:GetItemLink(bagID, slotID)
            end
        end

        if type(itemLink) ~= "string" or itemLink == "" then
            itemLink = ContainerItems:GetButtonItemLink(owner, bagID, slotID)
        end

        return owner, bagID, slotID, itemLink
    end

    function Runtime:TryShowForTooltip(tooltip, data)
        if self.enabled ~= true then
            self:HideButton()
            return
        end
        if tooltip ~= GameTooltip or tooltip:GetOwner() == self.button then
            return
        end
        if isCombatLocked() then
            self:HideButton()
            return
        end

        local owner, bagID, slotID, itemLink = self:GetTooltipContext(tooltip, data)
        if not isModifierActive() or not owner or not isPlayerBag(bagID) or not slotID or type(itemLink) ~= "string" then
            self:HideButton()
            return
        end

        local itemID = getItemIDFromLink(itemLink)
        local action = getProcessingAction(itemID, itemLink, bagID, slotID)
        if not action or not self:ApplyAttributes(action, bagID, slotID) then
            self:HideButton()
            return
        end

        self.owner = owner
        self.bagID = bagID
        self.slotID = slotID
        self.itemLink = itemLink
        self.action = action
        self:StyleButton(action)
        self.button:Show()
        self:AnchorButton()

        tooltip:AddLine(" ")
        tooltip:AddLine(
            string.format(
                L.FEATURE_ONE_CLICK_PROCESSING_TOOLTIP_USE or "%s + left click: %s",
                getModifierLabel(),
                action.label or ""
            ),
            action.color[1],
            action.color[2],
            action.color[3],
            true
        )
        tooltip:Show()
    end

    function Runtime:RefreshFromTooltip()
        if GameTooltip and GameTooltip:IsShown() and GameTooltip:GetOwner() then
            self:TryShowForTooltip(GameTooltip, nil)
        else
            self:HideButton()
        end
    end

    function Runtime:EnsureHooks()
        if self.hooksReady then
            return
        end
        self.hooksReady = true

        if TooltipDataProcessor and C_TooltipInfo and Enum and Enum.TooltipDataType then
            TooltipDataProcessor.AddTooltipPostCall(Enum.TooltipDataType.Item, function(tooltip, data)
                Runtime:TryShowForTooltip(tooltip, data)
            end)
        elseif type(hooksecurefunc) == "function" then
            hooksecurefunc(GameTooltip, "SetBagItem", function(tooltip, bagID, slotID)
                Runtime:TryShowForTooltip(tooltip, { bagID = bagID, slotID = slotID })
            end)
        end

        if not self.eventFrame then
            self.eventFrame = CreateFrame("Frame")
            self.eventFrame:SetScript("OnEvent", function(_, event)
                if event == "PLAYER_REGEN_ENABLED" then
                    if Runtime.hidePending then
                        Runtime:HideButton()
                    else
                        Runtime:RefreshFromTooltip()
                    end
                elseif event == "MODIFIER_STATE_CHANGED" then
                    Runtime:RefreshFromTooltip()
                else
                    Runtime:HideButton()
                end
            end)
        end
    end

    function Runtime:SetEnabled(enabled)
        self.enabled = enabled == true
        self:EnsureHooks()

        if self.eventFrame then
            self.eventFrame:UnregisterAllEvents()
            if self.enabled then
                self.eventFrame:RegisterEvent("MODIFIER_STATE_CHANGED")
                self.eventFrame:RegisterEvent("BAG_UPDATE_DELAYED")
                self.eventFrame:RegisterEvent("PLAYER_REGEN_DISABLED")
                self.eventFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
            end
        end

        if self.enabled then
            self:RefreshFromTooltip()
        else
            self:HideButton()
        end
    end

    ns:RegisterFeatureDefinition("one_click_processing", {
        id = "one_click_processing",
        order = 24,
        category = "inventory",
        title = function() return L.FEATURE_ONE_CLICK_PROCESSING_TITLE or "One-Click Processing" end,
        description = function() return L.FEATURE_ONE_CLICK_PROCESSING_DESCRIPTION or "Adds a modifier-click helper for Milling, Prospecting, Disenchanting, and Lockpicking." end,
        note = function() return L.FEATURE_ONE_CLICK_PROCESSING_NOTE or "Hold the selected modifier over a valid bag item, then left click the Vaultloom glow." end,
        previewLabel = function() return L.FEATURE_ONE_CLICK_PROCESSING_PREVIEW_LABEL or "Bag Action Glow" end,
        previewNote = function() return L.FEATURE_ONE_CLICK_PROCESSING_PREVIEW_NOTE or "Highlights processable bag items with a small colored action badge." end,
        optionToggle = {
            key = "modifier",
            label = function() return L.FEATURE_ONE_CLICK_PROCESSING_MODIFIER_LABEL or "Modifier" end,
            values = { "alt", "alt_shift", "alt_ctrl" },
            defaultValue = "alt",
            valueLabels = {
                alt = function() return L.FEATURE_ONE_CLICK_PROCESSING_MODIFIER_ALT or "Alt" end,
                alt_shift = function() return L.FEATURE_ONE_CLICK_PROCESSING_MODIFIER_ALT_SHIFT or "Alt + Shift" end,
                alt_ctrl = function() return L.FEATURE_ONE_CLICK_PROCESSING_MODIFIER_ALT_CTRL or "Alt + Ctrl" end,
            },
        },
        requiresReload = false,
        apply = function(isEnabled)
            Runtime:SetEnabled(isEnabled == true)
        end,
        onOptionChanged = function()
            Runtime:RefreshFromTooltip()
        end,
    })
end

registerOneClickProcessingFeature()
