--[[
    Commander 3.0 - Items Data Source

    Provides access to player inventory items.
    Commands: bags, bank, equipped
]]

local ADDON_NAME, C3 = ...

-- Use shared utilities
local fuzzyMatch = function(...) return C3.StringUtils.fuzzyMatch(...) end
local GetThemeColor = function(...) return C3.Colors.GetColor(...) end

-------------------------------------------------------------------------------
-- Item Type Registration
-------------------------------------------------------------------------------

C3.Types:Register("Item", {
    description = "Item from bags or equipment",
    source = "bags, equipped",
    properties = {
        { "id", "Item ID number" },
        { "name", "Item name" },
        { "link", "Clickable item link" },
        { "quality", "Quality: 0=Poor, 1=Common, 2=Uncommon, 3=Rare, 4=Epic, 5=Legendary" },
        { "qualityName", "Quality name: Poor, Common, Uncommon, etc." },
        { "ilvl", "Item level" },
        { "level", "Required player level" },
        { "type", "Item type: Armor, Weapon, Consumable, etc." },
        { "subtype", "Item subtype: Plate, Sword, Potion, etc." },
        { "icon", "Icon texture ID" },
        { "stackCount", "Current stack size" },
        { "maxStack", "Maximum stack size" },
        { "equipSlot", "Equipment slot: INVTYPE_HEAD, INVTYPE_CHEST, etc." },
        { "bag", "Bag index (0-4)" },
        { "slot", "Slot index within bag" },
        { "isBound", "true if soulbound" },
        { "isLocked", "true if locked" },
    },
    format = function(self)
        return self.link or self.name or "Unknown Item"
    end
})

-------------------------------------------------------------------------------
-- Quality Name Helper
-------------------------------------------------------------------------------

local QUALITY_NAMES = {
    [0] = "Poor",
    [1] = "Common",
    [2] = "Uncommon",
    [3] = "Rare",
    [4] = "Epic",
    [5] = "Legendary",
    [6] = "Artifact",
    [7] = "Heirloom",
    [8] = "WoW Token",
}

-------------------------------------------------------------------------------
-- Bag Index Constants
-------------------------------------------------------------------------------

-- WoW bag indices: 0=Backpack, 1-4=Regular bags, 5=Reagent bag
local BACKPACK_CONTAINER = 0
local NUM_BAG_SLOTS = NUM_BAG_SLOTS or 4  -- General purpose bags only (not reagent)
local REAGENTBAG_CONTAINER = Enum.BagIndex.ReagentBag or 5

-------------------------------------------------------------------------------
-- Items Intellisense Provider
-------------------------------------------------------------------------------

local MAX_SUGGESTIONS = C3.Constants.MAX_SUGGESTIONS

C3.Intellisense:RegisterProvider("Items", function(partial, ctx)
    local slotFilter = ctx and ctx.slot  -- e.g., "INVTYPE_HEAD"

    -- Collect all matching items first
    local items = {}
    local seen = {}  -- Deduplicate by name

    -- Search bags (0-4 + reagent bag)
    local bagsToSearch = {BACKPACK_CONTAINER}
    for i = 1, NUM_BAG_SLOTS do
        bagsToSearch[#bagsToSearch + 1] = i
    end
    bagsToSearch[#bagsToSearch + 1] = REAGENTBAG_CONTAINER

    for _, bag in ipairs(bagsToSearch) do
        local numSlots = C_Container.GetContainerNumSlots(bag)
        for slot = 1, numSlots do
            local itemInfo = C_Container.GetContainerItemInfo(bag, slot)
            if itemInfo and itemInfo.itemID then
                local name, _, quality, _, _, _, _, _, equipLoc = C_Item.GetItemInfo(itemInfo.itemID)
                if name and (not slotFilter or equipLoc == slotFilter) and not seen[name] then
                    if fuzzyMatch(partial, name) then
                        items[#items + 1] = {
                            name = name,
                            equipLoc = equipLoc,
                            quality = quality,
                            equipped = false,
                        }
                        seen[name] = true
                        if #items >= MAX_SUGGESTIONS then break end
                    end
                end
            end
        end
        if #items >= MAX_SUGGESTIONS then break end
    end

    -- Search equipped items
    if #items < MAX_SUGGESTIONS then
        local EQUIP_SLOTS = {1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}
        for _, slotId in ipairs(EQUIP_SLOTS) do
            local itemID = GetInventoryItemID("player", slotId)
            if itemID then
                local name, _, quality, _, _, _, _, _, equipLoc = C_Item.GetItemInfo(itemID)
                if name and (not slotFilter or equipLoc == slotFilter) and not seen[name] then
                    if fuzzyMatch(partial, name) then
                        items[#items + 1] = {
                            name = name,
                            equipLoc = equipLoc,
                            quality = quality,
                            equipped = true,
                        }
                        seen[name] = true
                        if #items >= MAX_SUGGESTIONS then break end
                    end
                end
            end
        end
    end

    local results = {}
    for _, item in ipairs(items) do
        local desc = item.equipLoc or "Item"
        if item.equipped then
            desc = desc .. " |cFF00FF00[Equipped]|r"
        end
        results[#results + 1] = {
            text = item.name,
            description = desc,
            quality = item.quality,
        }
    end
    return results
end)

-------------------------------------------------------------------------------
-- bags - List inventory items
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "bags",
    description = "List items in bags",
    category = "data",
    producesType = "Item",
    acceptsInput = false,
    flags = {
        empty = { short = "e", description = "Include empty slots" },
        all = { short = "a", description = "Include all bag slots" },
        grid = { short = "g", description = "Show as icon grid" },
    },
    execute = function(ctx)
        local includeEmpty = ctx:HasFlag("empty") or ctx:HasFlag("e")
        local showGrid = ctx:HasFlag("grid") or ctx:HasFlag("g")

        -- Bags to search: 0-4 + reagent bag
        local bagsToSearch = {BACKPACK_CONTAINER}
        for i = 1, NUM_BAG_SLOTS do
            bagsToSearch[#bagsToSearch + 1] = i
        end
        bagsToSearch[#bagsToSearch + 1] = REAGENTBAG_CONTAINER

        -- Helper to build a single item object from bag/slot
        local function buildItem(bag, slot)
            local itemInfo = C_Container.GetContainerItemInfo(bag, slot)

            if itemInfo and itemInfo.itemID then
                local name, link, quality, ilvl, reqLevel, itemType, itemSubType,
                      maxStack, equipSlot, icon = C_Item.GetItemInfo(itemInfo.itemID)

                if not name then
                    name = itemInfo.hyperlink and itemInfo.hyperlink:match("%[(.-)%]") or "Unknown"
                    link = itemInfo.hyperlink
                    quality = itemInfo.quality or 1
                elseif itemInfo.hyperlink then
                    local linkName = itemInfo.hyperlink:match("%[(.-)%]")
                    if linkName and linkName ~= "" and linkName ~= name then
                        name = linkName
                    end
                end

                local itemIcon = icon
                    or itemInfo.iconFileID
                    or C_Item.GetItemIconByID(itemInfo.itemID)

                return {
                    id = itemInfo.itemID,
                    itemId = itemInfo.itemID,
                    name = name or "Unknown",
                    link = link or itemInfo.hyperlink,
                    itemLink = link or itemInfo.hyperlink,
                    quality = quality or 1,
                    qualityName = QUALITY_NAMES[quality or 1] or "Unknown",
                    ilvl = ilvl or 0,
                    level = reqLevel or 0,
                    type = itemType or "",
                    subtype = itemSubType or "",
                    icon = itemIcon,
                    stackCount = itemInfo.stackCount or 1,
                    count = itemInfo.stackCount or 1,
                    maxStack = maxStack or 1,
                    equipSlot = equipSlot or "",
                    bag = bag,
                    slot = slot,
                    isBound = itemInfo.isBound or false,
                    isLocked = itemInfo.isLocked or false,
                    _type = "Item",
                }
            elseif includeEmpty then
                return C3.Types.Create("EmptySlot", {
                    bag = bag,
                    slot = slot,
                    display = string.format("Empty [Bag %d, Slot %d]", bag, slot)
                })
            end
            return nil
        end

        -- Grid mode needs the full array materialized
        if showGrid then
            local items = {}
            for _, bag in ipairs(bagsToSearch) do
                local numSlots = C_Container.GetContainerNumSlots(bag)
                for slot = 1, numSlots do
                    local item = buildItem(bag, slot)
                    if item then
                        items[#items + 1] = item
                    end
                end
            end
            return ctx:IconGrid(items)
        end

        -- Lazy producer: items built on demand (take 5 only builds 5)
        return ctx:Produce(function(emit)
            for _, bag in ipairs(bagsToSearch) do
                local numSlots = C_Container.GetContainerNumSlots(bag)
                for slot = 1, numSlots do
                    local item = buildItem(bag, slot)
                    if item then
                        emit(item)
                    end
                end
            end
        end)
    end
})

-------------------------------------------------------------------------------
-- equipped - List equipped items
-------------------------------------------------------------------------------

local EQUIP_SLOTS = {
    { id = 1,  name = "Head" },
    { id = 2,  name = "Neck" },
    { id = 3,  name = "Shoulder" },
    { id = 5,  name = "Chest" },
    { id = 6,  name = "Waist" },
    { id = 7,  name = "Legs" },
    { id = 8,  name = "Feet" },
    { id = 9,  name = "Wrist" },
    { id = 10, name = "Hands" },
    { id = 11, name = "Finger0" },
    { id = 12, name = "Finger1" },
    { id = 13, name = "Trinket0" },
    { id = 14, name = "Trinket1" },
    { id = 15, name = "Back" },
    { id = 16, name = "MainHand" },
    { id = 17, name = "OffHand" },
}

C3.Command:Define({
    name = "equipped",
    description = "List equipped items",
    category = "data",
    producesType = "Item",
    acceptsInput = false,
    flags = {
        empty = { short = "e", description = "Include empty slots" },
    },
    execute = function(ctx)
        local includeEmpty = ctx:HasFlag("empty") or ctx:HasFlag("e")

        return ctx:Produce(function(emit)
            for _, slotInfo in ipairs(EQUIP_SLOTS) do
                local itemID = GetInventoryItemID("player", slotInfo.id)

                if itemID then
                    local name, link, quality, ilvl, reqLevel, itemType, itemSubType,
                          maxStack, equipSlot, icon = C_Item.GetItemInfo(itemID)
                    local itemLink = GetInventoryItemLink("player", slotInfo.id)

                    -- Prefer name from link as it might be more complete (suffixes etc)
                    if itemLink then
                        local linkName = itemLink:match("%[(.-)%]")
                        if linkName and linkName ~= "" then
                            name = linkName
                        end
                        link = itemLink
                    end

                    -- Fallback for uncached items
                    if not name then
                        name = itemLink and itemLink:match("%[(.-)%]") or "Unknown"
                        link = itemLink
                    end

                    emit(C3.Types.Create("Item", {
                        id = itemID,
                        name = name or "Unknown",
                        link = link or "",
                        quality = quality or 1,
                        qualityName = QUALITY_NAMES[quality or 1] or "Unknown",
                        ilvl = ilvl or 0,
                        level = reqLevel or 0,
                        type = itemType or "",
                        subtype = itemSubType or "",
                        icon = icon or GetInventoryItemTexture("player", slotInfo.id),
                        stackCount = 1,
                        maxStack = 1,
                        equipSlot = slotInfo.name,
                        slot = slotInfo.id,
                        isBound = true,
                        isLocked = false,
                    }))
                elseif includeEmpty then
                    emit(C3.Types.Create("EmptySlot", {
                        slot = slotInfo.id,
                        slotName = slotInfo.name,
                        display = string.format("Empty [%s]", slotInfo.name)
                    }))
                end
            end
        end)
    end
})

-------------------------------------------------------------------------------
-- item - Get specific item by name or ID
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "item",
    description = "Get item info by name or ID",
    category = "data",
    acceptsInput = false,
    args = {
        { name = "query", required = true, description = "Item name or ID" }
    },
    execute = function(ctx)
        local query = ctx:GetArg(1)
        if not query then
            return ctx:Error("Usage: item <name or id>")
        end

        -- Try as item ID first
        local itemID = tonumber(query)

        -- Search by name in bags if not a numeric ID
        if not itemID then
            local queryLower = query:lower()

            -- Search all bags (0-4 + reagent bag)
            local searchBags = {BACKPACK_CONTAINER}
            for i = 1, NUM_BAG_SLOTS do
                searchBags[#searchBags + 1] = i
            end
            searchBags[#searchBags + 1] = REAGENTBAG_CONTAINER

            for _, bag in ipairs(searchBags) do
                if itemID then break end

                local numSlots = C_Container.GetContainerNumSlots(bag)

                for slot = 1, numSlots do repeat
                    if itemID then break end

                    local itemInfo = C_Container.GetContainerItemInfo(bag, slot)

                    -- Guard: No item in slot
                    if not itemInfo or not itemInfo.itemID then break end

                    local name = C_Item.GetItemInfo(itemInfo.itemID)

                    -- Guard: No name or no match
                    if not name then break end
                    if name:lower() ~= queryLower then break end

                    -- Found match
                    itemID = itemInfo.itemID
                until true end
            end
        end

        if not itemID then
            return ctx:Error("Item not found: " .. query)
        end

        local name, link, quality, ilvl, reqLevel, itemType, itemSubType,
              maxStack, equipSlot, icon = C_Item.GetItemInfo(itemID)

        if not name then
            return ctx:Error("Item not cached: " .. query)
        end

        return C3.Types.Create("Item", {
            id = itemID,
            name = name,
            link = link,
            quality = quality,
            qualityName = QUALITY_NAMES[quality] or "Unknown",
            ilvl = ilvl,
            level = reqLevel,
            type = itemType,
            subtype = itemSubType,
            icon = icon,
            maxStack = maxStack,
            equipSlot = equipSlot,
        })
    end
})

-------------------------------------------------------------------------------
-- durability - Get equipment durability
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "durability",
    description = "Show equipment durability",
    category = "data",
    acceptsInput = false,
    execute = function(ctx)
        return ctx:Produce(function(emit)
            for _, slotInfo in ipairs(EQUIP_SLOTS) do
                local current, maximum = GetInventoryItemDurability(slotInfo.id)

                if current and maximum then
                    local percent = math.floor((current / maximum) * 100)
                    local itemID = GetInventoryItemID("player", slotInfo.id)
                    local name = "Unknown"

                    if itemID then
                        name = C_Item.GetItemInfo(itemID) or "Unknown"
                    end

                    emit(C3.Types.Create("Durability", {
                        slot = slotInfo.id,
                        slotName = slotInfo.name,
                        name = name,
                        current = current,
                        max = maximum,
                        percent = percent,
                        display = string.format("%s: %d/%d (%d%%)", slotInfo.name, current, maximum, percent)
                    }))
                end
            end
        end)
    end
})

-------------------------------------------------------------------------------
-- Register Durability type
-------------------------------------------------------------------------------

C3.Types:Register("Durability", {
    description = "Equipment durability status",
    source = "durability",
    properties = {
        { "slot", "Equipment slot ID" },
        { "slotName", "Slot name: Head, Chest, etc." },
        { "name", "Item name" },
        { "current", "Current durability" },
        { "max", "Maximum durability" },
        { "percent", "Durability percent (0-100)" },
        { "display", "Formatted display string" },
    },
    format = function(self)
        return self.display
    end
})

-------------------------------------------------------------------------------
-- destroy - Destroy items (permanent deletion)
-------------------------------------------------------------------------------

-- Helper to format item display with stack count, ilvl, and equipped status
local function formatItemDisplay(item)
    local display = item.link or item.name or "Unknown"

    -- Add stack count if > 1
    if item.stackCount and item.stackCount > 1 then
        display = display .. "|cFFD8DEE9x" .. item.stackCount .. "|r"  -- nord4
    end

    -- Add EQUIPPED tag before ilvl
    if item.equipped then
        display = display .. "|cFFBF616A[EQUIPPED]|r"  -- nord11 red
    end

    -- Add ilvl for gear (ilvl > 1 typically means it's gear)
    if item.ilvl and item.ilvl > 1 then
        display = display .. "|cFF4C566A(" .. item.ilvl .. " ilvl)|r"  -- nord3 gray
    end

    return display
end

-- Normalize string for matching (keep only alphanumeric, remove all spaces/punctuation)
local function normalizeForMatch(str)
    if not str then return "" end
    -- Remove all non-alphanumeric (including spaces), then lowercase
    return str:gsub("[^%w]", ""):lower()
end

-- Helper to find items by name (searches bags AND equipped)
local function findItemsByName(query)
    if not query or query == "" then return {} end

    local queryNorm = normalizeForMatch(query)
    local results = {}

    -- Search all bags (0-4 + reagent bag)
    local searchBags = {BACKPACK_CONTAINER}
    for i = 1, NUM_BAG_SLOTS do
        searchBags[#searchBags + 1] = i
    end
    searchBags[#searchBags + 1] = REAGENTBAG_CONTAINER

    for _, bag in ipairs(searchBags) do
        local numSlots = C_Container.GetContainerNumSlots(bag)

        for slot = 1, numSlots do
            local itemInfo = C_Container.GetContainerItemInfo(bag, slot)

            if itemInfo and itemInfo.itemID then
                local name, link, quality, ilvl = C_Item.GetItemInfo(itemInfo.itemID)
                
                -- Prefer name from link as it might be more complete (suffixes etc)
                if itemInfo.hyperlink then
                    local linkName = itemInfo.hyperlink:match("%[(.-)%]")
                    if linkName and linkName ~= "" then
                        name = linkName
                    end
                end
                
                name = name or "Unknown"

                local nameNorm = normalizeForMatch(name)
                if nameNorm == queryNorm then
                    table.insert(results, {
                        id = itemInfo.itemID,
                        name = name,
                        link = itemInfo.hyperlink or link,
                        bag = bag,
                        slot = slot,
                        quality = itemInfo.quality or quality or 1,
                        ilvl = ilvl or 0,
                        stackCount = itemInfo.stackCount or 1,
                        equipped = false,
                    })
                end
            end
        end
    end

    -- Search equipped items (same approach as 'equipped' command)
    for _, slotInfo in ipairs(EQUIP_SLOTS) do
        local itemID = GetInventoryItemID("player", slotInfo.id)

        if itemID then
            local name, link, quality, ilvl = C_Item.GetItemInfo(itemID)
            local itemLink = GetInventoryItemLink("player", slotInfo.id)

            -- Prefer name from link as it might be more complete (suffixes etc)
            if itemLink then
                local linkName = itemLink:match("%[(.-)%]")
                if linkName and linkName ~= "" then
                    name = linkName
                end
                link = itemLink
            end

            -- Fallback for uncached items if name still nil
            if not name then
                name = itemLink and itemLink:match("%[(.-)%]") or nil
                link = itemLink
            end

            if name then
                local nameNorm = normalizeForMatch(name)
                if nameNorm == queryNorm then
                    table.insert(results, {
                        id = itemID,
                        name = name,
                        link = link or GetInventoryItemLink("player", slotInfo.id),
                        equipSlotId = slotInfo.id,
                        equipSlotName = slotInfo.name,
                        quality = quality or 1,
                        ilvl = ilvl or 0,
                        stackCount = 1,
                        equipped = true,
                    })
                end
            end
        end
    end

    return results
end

-- Helper to destroy a single item
local function destroyItem(item)
    if InCombatLockdown and InCombatLockdown() then
        return false, "Cannot destroy items during combat"
    end

    local function deleteByLocation(location)
        if C_Item and C_Item.DeleteItem and location and location:IsValid() then
            C_Item.DeleteItem(location)
            return true
        end
        return false
    end

    if item.equipped then
        -- Equipped item: need to pick up from inventory slot
        if not item.equipSlotId then return false, "Invalid equipped item" end

        -- Check if item still exists
        local itemID = GetInventoryItemID("player", item.equipSlotId)
        if not itemID or itemID ~= item.id then
            return false, "Item moved or no longer exists"
        end

        -- Prefer direct delete by location if available
        local equipLocation = ItemLocation:CreateFromEquipmentSlot(item.equipSlotId)
        if deleteByLocation(equipLocation) then
            return true
        end

        -- Fallback: Destroy equipped item via cursor
        ClearCursor()
        PickupInventoryItem(item.equipSlotId)
        DeleteCursorItem()

        return true
    else
        -- Bag item
        if not item.bag or not item.slot then return false, "Invalid item location" end

        -- Check if item still exists in that location
        local itemInfo = C_Container.GetContainerItemInfo(item.bag, item.slot)
        if not itemInfo or itemInfo.itemID ~= item.id then
            return false, "Item moved or no longer exists"
        end

        -- Check if item is locked
        local location = ItemLocation:CreateFromBagAndSlot(item.bag, item.slot)
        if location and C_Item.IsLocked(location) then
            return false, "Item is locked"
        end

        -- Prefer direct delete by location if available
        if deleteByLocation(location) then
            return true
        end

        -- Prefer bag delete API if available
        if C_Container and C_Container.DeleteContainerItem then
            C_Container.DeleteContainerItem(item.bag, item.slot)
            return true
        end

        -- Fallback: Destroy the item (requires hardware event - called from Y keypress)
        ClearCursor()
        C_Container.PickupContainerItem(item.bag, item.slot)
        DeleteCursorItem()

        return true
    end
end

-- Helper for --force mode: destroy all items with latency spacing
local function destroyWithLatency(items, buffer)
    local index = 0
    local destroyed = {}
    local errors = {}

    local function destroyNext()
        index = index + 1
        local item = items[index]

        if not item then
            -- All done
            if buffer then
                if #destroyed > 0 then
                    local msg = #destroyed == 1
                        and ("Destroyed: " .. destroyed[1])
                        or ("Destroyed " .. #destroyed .. " items")
                    buffer:AddLine(C3.BufferLine.Text("|cFFA3BE8C" .. msg .. "|r"))  -- nord14 green
                end
                if #errors > 0 then
                    buffer:AddLine(C3.BufferLine.Text(
                        "|cFFBF616AFailed: " .. #errors .. " items|r"  -- nord11 red
                    ))
                end
            end
            return
        end

        local success, err = destroyItem(item)
        if success then
            table.insert(destroyed, formatItemDisplay(item))
        else
            table.insert(errors, { item = item, error = err })
        end

        -- Continue after latency delay
        C_Timer.After(0.15, destroyNext)
    end

    -- Show list first
    if buffer then
        buffer:AddLine(C3.BufferLine.Text(
            string.format("|cFFFF5555Destroying %d item(s)...|r", #items)
        ))
    end

    -- Start destruction
    destroyNext()

    return { _type = "PromptQueue" }  -- Signal that we're handling output
end

C3.Command:Define({
    name = "destroy",
    description = "Destroy items permanently",
    category = "action",
    flags = {
        { name = "force", short = "f", description = "Skip confirmation prompts" }
    },
    args = {
        { name = "item", required = false, description = "Item name to destroy",
          provider = "Items" }
    },
    execute = function(ctx)
        local force = ctx:HasFlag("force") or ctx:HasFlag("f")
        local toDestroy = {}

        -- Collect items from pipeline or argument
        if ctx:HasInput() then
            ctx:Consume(function(obj)
                if obj and obj._type == "Item" then
                    local link = obj.link
                    if obj.bag ~= nil and obj.slot ~= nil then
                        link = C_Container.GetContainerItemLink(obj.bag, obj.slot) or link
                    elseif obj.bag == nil and obj.slot ~= nil then
                        link = GetInventoryItemLink("player", obj.slot) or link
                    end

                    -- Items from bags have bag/slot, equipped items have equipSlot
                    table.insert(toDestroy, {
                        id = obj.id,
                        name = obj.name,
                        link = link,
                        bag = obj.bag,
                        slot = obj.slot,
                        equipSlotId = obj.slot and not obj.bag and obj.slot or nil,  -- equipped items use slot as equipSlotId
                        quality = obj.quality,
                        ilvl = obj.ilvl or 0,
                        stackCount = obj.stackCount or 1,
                        equipped = obj.bag == nil and obj.slot ~= nil,  -- equipped if no bag but has slot
                    })
                end
            end)
        else
            local query = table.concat(ctx:GetArgs(), " ")
            if not query or query == "" then
                return ctx:Error("Usage: destroy <item name> or pipe items from bags")
            end

            -- Find all matching items (bags + equipped)
            local items = findItemsByName(query)
            for _, item in ipairs(items) do
                table.insert(toDestroy, item)
            end
        end

        if #toDestroy == 0 then
            return ctx:Error("No items found to destroy")
        end

        -- With --force: destroy with latency spacing (no prompts)
        if force then
            local buffer = ctx.session and ctx.session._activeBuffer
            if not buffer and C3.Terminal and C3.Terminal.output then
                buffer = C3.Terminal.output.buffer
            end
            return destroyWithLatency(toDestroy, buffer)
        end

        -- Without --force: return queue request (terminal handles it)
        return C3.Prompt.QueueRequest({
            items = toDestroy,
            buildMessage = function(item, index, total)
                local itemDisplay = formatItemDisplay(item)
                if total == 1 then
                    return string.format("Destroy %s?", itemDisplay)
                else
                    return string.format("Destroy %s? (%d of %d)", itemDisplay, index, total)
                end
            end,
            onConfirm = function(item)
                local success, err = destroyItem(item)
                if not success then
                    return C3.Types.Create("Text", {
                        content = "|cFFFF5555Failed:|r " .. (err or "Unknown error")
                    })
                end
                return nil
            end,
            onCancel = function(remaining)
                if #remaining > 0 then
                    return C3.Types.Create("Text", {
                        content = string.format("|cFF888888Cancelled (%d items skipped)|r", #remaining)
                    })
                end
            end,
            onComplete = function(destroyed, skipped)
                if #destroyed > 1 then
                    return C3.Types.Create("Text", {
                        content = string.format("|cFF00FF00Destroyed %d items|r", #destroyed)
                    })
                end
            end,
        })
    end
})

-------------------------------------------------------------------------------
-- ItemLevel Type Registration
-------------------------------------------------------------------------------

C3.Types:Register("ItemLevel", {
    description = "Player item level information",
    source = "ilvl",
    properties = {
        { "equipped", "Equipped item level" },
        { "overall", "Overall item level (including bags)" },
    },
})

C3.Formatter:Register("ItemLevel", {
    format = function(obj)
        local equippedColor = GetThemeColor("accent.success") or "A3BE8C"
        local overallColor = GetThemeColor("accent.info") or "81A1C1"
        local dimColor = GetThemeColor("status.dim") or "4C566A"

        local equippedStr = string.format("|cFF%s%.1f|r", equippedColor, obj.equipped or 0)
        local overallStr = string.format("|cFF%s%.1f|r", overallColor, obj.overall or 0)

        return string.format("Item Level: %s equipped  |cFF%s(|r%s overall|cFF%s)|r",
            equippedStr, dimColor, overallStr, dimColor)
    end,
    list = function(obj)
        return string.format("ilvl: %.1f / %.1f", obj.equipped or 0, obj.overall or 0)
    end,
    compact = function(obj)
        return string.format("%.1f", obj.equipped or 0)
    end
})

-------------------------------------------------------------------------------
-- ilvl - Show player item level
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "ilvl",
    description = "Show player item level",
    category = "data",
    acceptsInput = false,
    execute = function(ctx)
        local overall, equipped = GetAverageItemLevel()
        return C3.Types.Create("ItemLevel", {
            equipped = equipped or 0,
            overall = overall or 0,
        })
    end
})

return true
