---@diagnostic disable: undefined-field, undefined-global
local _, namespace = ...

local WTUI = namespace.WildThingsUI
local Theme = namespace.Theme

-- Compact pill dimensions (Claude's discretion within the wild-things-design tokens).
local PILL_HEIGHT = 22
local PAD_LEFT = 6
local PAD_RIGHT = 6
local GAP_INNER = 4
local X_SIZE = 12
local X_HIT = 16 -- comfortable hit area, larger than the visible glyph
-- Inline required-charges selector's fixed width (PTW-01). Wide enough for a
-- 1-2 digit charge count plus the dropdown's own chevron box.
local CHARGES_DD_WIDTH = 48

-- namespace:CreateCooldownPill(parent, spellID, onRemove, specID)
-- Compact icon + name + X pill (READY-05 Settings redesign). Brand-skinned per
-- .claude/skills/wild-things-design/README.md: 1px BORDER_STRONG border + SURFACE_2 fill
-- (WoW textures cannot truly round corners with no rounded-corner asset in the repo, so
-- this IS the practical "pill" look here — matches Dropdown.lua/SpellIcon.lua skinning).
-- The X tints Theme.TEXT_DIM -> Theme.DANGER (#FF5262) on hover and calls onRemove() on
-- click. Sizes itself to fit its own content; callers read pill:GetWidth()/GetHeight()
-- for wrap math (SpecCooldownPicker.lua's picker:Layout()).
-- specID gates the inline required-charges dropdown (PTW-01): absent = no
-- dropdown regardless of charge state, since there is no SavedVars path to
-- read or write. For spells with C_Spell.GetSpellCharges reporting
-- maxCharges > 1 the dropdown renders inline between name and X; single-
-- charge spells and trinket pills (CreateItemPill below) are unaffected.
function namespace:CreateCooldownPill(parent, spellID, onRemove, specID)
    local pill = CreateFrame("Frame", nil, parent)
    -- Resolve whether this spell needs the inline charges control BEFORE
    -- the height is committed (PILL_HEIGHT vs Theme.H_DROPDOWN). Theme.H_DROPDOWN
    -- is 28, PILL_HEIGHT is 22; every sub-widget below anchors via a single
    -- LEFT/CENTER point, which recenters automatically inside a taller frame
    -- with no further change needed. The specID guard is defensive -- without
    -- it there is nowhere to read or write the threshold.
    local chargeInfo = C_Spell.GetSpellCharges(spellID)
    local maxCharges = chargeInfo and chargeInfo.maxCharges
    local hasChargesControl = specID and maxCharges and maxCharges > 1
    pill:SetHeight(hasChargesControl and math.max(PILL_HEIGHT, Theme.H_DROPDOWN) or PILL_HEIGHT)
    pill.spellID = spellID

    -- EnableMouse is the hard prerequisite for the spell tooltip below: a plain Frame
    -- receives no OnEnter/OnLeave (and so WTUI:AddTooltip never fires) without it
    -- (same gotcha documented at GroupGrid.lua:387-391).
    pill:EnableMouse(true)

    -- Background fill (BACKGROUND layer, inset 1px so the rounded border ring below shows
    -- through as a 1px band).
    pill.bg = pill:CreateTexture(nil, "BACKGROUND")
    pill.bg:SetPoint("TOPLEFT", pill, "TOPLEFT", 1, -1)
    pill.bg:SetPoint("BOTTOMRIGHT", pill, "BOTTOMRIGHT", -1, 1)
    pill.bg:SetColorTexture(Theme.SURFACE_2.r, Theme.SURFACE_2.g, Theme.SURFACE_2.b, Theme.SURFACE_2.a)

    -- Rounded overlay-clip primitive (ROUND-04, spike-002): flat 4px radius per the locked
    -- control-radius decision (NOT a true height/2 capsule -- Assumption A2).
    WTUI:AddRoundedMask(pill, Theme.RADIUS_SM)
    pill.bg:AddMaskTexture(pill.roundedMask)

    -- Rounded border ring (ROUND-BORDER-01): follows the corner arcs instead of a square stroke.
    WTUI:AddRoundedBorder(pill, Theme.RADIUS_SM, 1)
    pill:SetBorderColor(Theme.BORDER_STRONG.r, Theme.BORDER_STRONG.g, Theme.BORDER_STRONG.b, Theme.BORDER_STRONG.a)

    -- Spell info (nil-guarded per SpellIcon.lua's invalid-spell fallback -- a candidate's
    -- spellID could go stale before the Python generator is re-run).
    local info = WTUI.GetSpellInfo(spellID)
    local iconID = info and info.iconID or 134400
    local spellName = (info and info.name) or tostring(spellID)

    -- 1. Icon.
    pill.icon = pill:CreateTexture(nil, "ARTWORK")
    pill.icon:SetSize(Theme.ICON_SM, Theme.ICON_SM)
    pill.icon:SetPoint("LEFT", pill, "LEFT", PAD_LEFT, 0)
    pill.icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
    pill.icon:SetTexture(iconID)
    pill.icon:SetSnapToPixelGrid(false)
    pill.icon:SetTexelSnappingBias(0)

    -- 2. Name.
    pill.name = pill:CreateFontString(nil, "ARTWORK")
    pill.name:SetFontObject(WildThingsFont13)
    pill.name:SetTextColor(Theme.TEXT.r, Theme.TEXT.g, Theme.TEXT.b, Theme.TEXT.a)
    pill.name:SetText(spellName)
    pill.name:SetPoint("LEFT", pill.icon, "RIGHT", GAP_INNER, 0)

    -- 2b. Required-charges selector (PTW-01). Gated on hasChargesControl
    -- (maxCharges > 1 AND specID present). Reads/writes
    -- WildThingsSavedVars.readycheck.cooldown.per_spec_required_charges --
    -- a SEPARATE sparse table from per_spec (see Migrations.lua). Clamp
    -- defensively: a hand-edited or stale SavedVars value can otherwise
    -- index past the option list built below. Each option's .value is its
    -- own 1-based position in the flat infoTable, so initialValue = {stored}
    -- resolves directly via CreateDropdown's SetValue.
    if hasChargesControl then
        local perRequired = WildThingsSavedVars.readycheck.cooldown.per_spec_required_charges
        local stored = perRequired[specID] and perRequired[specID][spellID] or 1
        if type(stored) ~= "number" or stored < 1 or stored > maxCharges then stored = 1 end

        local optionsTable = {}
        for n = 1, maxCharges do
            optionsTable[n] = { text = tostring(n), value = n }
        end

        pill.chargesDropdown = namespace:CreateDropdown(pill, nil, optionsTable,
            function(n) perRequired[specID] = perRequired[specID] or {}; perRequired[specID][spellID] = n end,
            { stored })
        -- Height MUST stay at Theme.H_DROPDOWN -- Dropdown.lua's LRArrowFrame
        -- chevron box is sized at construction time and is never re-sized by
        -- a later SetSize on the outer control, so a shorter height would
        -- leave the arrow box visibly taller than its parent.
        pill.chargesDropdown:SetSize(CHARGES_DD_WIDTH, Theme.H_DROPDOWN)
        pill.chargesDropdown:SetPoint("LEFT", pill.name, "RIGHT", GAP_INNER, 0)
    end

    -- 3. X remove button.
    pill.removeBtn = CreateFrame("Button", nil, pill)
    pill.removeBtn:SetSize(X_HIT, X_HIT)
    pill.removeBtn:SetPoint("LEFT", hasChargesControl and pill.chargesDropdown or pill.name, "RIGHT", GAP_INNER, 0)

    pill.removeBtn.tex = pill.removeBtn:CreateTexture(nil, "ARTWORK")
    pill.removeBtn.tex:SetSize(X_SIZE, X_SIZE)
    pill.removeBtn.tex:SetPoint("CENTER")
    pill.removeBtn.tex:SetTexture(Theme.GetIconPath("x"))
    pill.removeBtn.tex:SetVertexColor(Theme.TEXT_DIM.r, Theme.TEXT_DIM.g, Theme.TEXT_DIM.b, Theme.TEXT_DIM.a)
    pill.removeBtn.tex:SetSnapToPixelGrid(false)
    pill.removeBtn.tex:SetTexelSnappingBias(0)

    pill.removeBtn:SetScript("OnEnter", function()
        pill.removeBtn.tex:SetVertexColor(Theme.DANGER.r, Theme.DANGER.g, Theme.DANGER.b, Theme.DANGER.a)
    end)
    pill.removeBtn:SetScript("OnLeave", function()
        pill.removeBtn.tex:SetVertexColor(Theme.TEXT_DIM.r, Theme.TEXT_DIM.g, Theme.TEXT_DIM.b, Theme.TEXT_DIM.a)
    end)
    pill.removeBtn:SetScript("OnClick", function() onRemove() end)

    -- Size the pill to fit its content: leftPad + icon + gap + name + [gap +
    -- charges-dropdown] + gap + X + rightPad. The charges-dropdown term is
    -- inserted in the same position the dropdown itself is anchored above.
    local width = PAD_LEFT + Theme.ICON_SM + GAP_INNER + pill.name:GetStringWidth()
        + (hasChargesControl and (CHARGES_DD_WIDTH + GAP_INNER) or 0)
        + GAP_INNER + X_HIT + PAD_RIGHT
    pill:SetWidth(width)

    -- Real WoW spell tooltip on hover (like hovering a spell in the spellbook):
    -- GameTooltip:SetSpellByID renders the full name/cast/range/cooldown/description block,
    -- not just a plain-text label. EnableMouse above is the prerequisite for OnEnter/OnLeave.
    pill:SetScript("OnEnter", function()
        GameTooltip:SetOwner(pill, "ANCHOR_RIGHT")
        GameTooltip:SetSpellByID(spellID)
        GameTooltip:Show()
    end)
    pill:SetScript("OnLeave", function()
        GameTooltip:Hide()
    end)

    return pill
end

-- Which of the caller-declared equipment slots currently holds itemID, or nil.
-- Only the declared slots are scanned -- a pill for an item that is never worn
-- (bank/consumable use) passes no slots and skips this entirely.
local function FindEquippedSlot(itemID, slots)
    if not slots then return nil end

    for i = 1, #slots do
        if GetInventoryItemID("player", slots[i]) == itemID then
            return slots[i]
        end
    end

    return nil
end

-- namespace:CreateItemPill(parent, itemID, onRemove, equipSlots)
-- equipSlots is OPTIONAL: a list of equipment slot IDs this item can occupy (e.g.
-- { INVSLOT_TRINKET1, INVSLOT_TRINKET2 }). Passing it upgrades the hover tooltip
-- from the item's base variant to the equipped one, which is what carries the real
-- item level. Omitting it keeps the previous behaviour.
-- Compact icon + name + X pill (WW1-BANK-ORGANIZE) -- a sibling of CreateCooldownPill
-- above, identical layout/skinning, but resolving display data from the ITEM rather
-- than the spell.
function namespace:CreateItemPill(parent, itemID, onRemove, equipSlots)
    local pill = CreateFrame("Frame", nil, parent)
    pill:SetHeight(PILL_HEIGHT)
    pill.itemID = itemID

    -- EnableMouse is the hard prerequisite for the tooltip below (same gotcha
    -- documented on CreateCooldownPill above / GroupGrid.lua:387-391).
    pill:EnableMouse(true)

    pill.bg = pill:CreateTexture(nil, "BACKGROUND")
    pill.bg:SetPoint("TOPLEFT", pill, "TOPLEFT", 1, -1)
    pill.bg:SetPoint("BOTTOMRIGHT", pill, "BOTTOMRIGHT", -1, 1)
    pill.bg:SetColorTexture(Theme.SURFACE_2.r, Theme.SURFACE_2.g, Theme.SURFACE_2.b, Theme.SURFACE_2.a)

    WTUI:AddRoundedMask(pill, Theme.RADIUS_SM)
    pill.bg:AddMaskTexture(pill.roundedMask)

    -- Rounded border ring (ROUND-BORDER-01): follows the corner arcs instead of a square stroke.
    WTUI:AddRoundedBorder(pill, Theme.RADIUS_SM, 1)
    pill:SetBorderColor(Theme.BORDER_STRONG.r, Theme.BORDER_STRONG.g, Theme.BORDER_STRONG.b, Theme.BORDER_STRONG.a)

    -- Item info (nil-guarded -- GetItemInfoInstant resolves the icon even for an
    -- item the client hasn't fully cached yet; the name falls back to a raw
    -- "item:<id>" label until the client caches it, per the plan's ASSUMED default).
    local _, _, _, _, iconID = GetItemInfoInstant(itemID)
    iconID = iconID or 134400
    local itemName = GetItemInfo(itemID) or ("item:" .. tostring(itemID))

    -- 1. Icon.
    pill.icon = pill:CreateTexture(nil, "ARTWORK")
    pill.icon:SetSize(Theme.ICON_SM, Theme.ICON_SM)
    pill.icon:SetPoint("LEFT", pill, "LEFT", PAD_LEFT, 0)
    pill.icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
    pill.icon:SetTexture(iconID)
    pill.icon:SetSnapToPixelGrid(false)
    pill.icon:SetTexelSnappingBias(0)

    -- 2. Name.
    pill.name = pill:CreateFontString(nil, "ARTWORK")
    pill.name:SetFontObject(WildThingsFont13)
    pill.name:SetTextColor(Theme.TEXT.r, Theme.TEXT.g, Theme.TEXT.b, Theme.TEXT.a)
    pill.name:SetText(itemName)
    pill.name:SetPoint("LEFT", pill.icon, "RIGHT", GAP_INNER, 0)

    -- 3. X remove button.
    pill.removeBtn = CreateFrame("Button", nil, pill)
    pill.removeBtn:SetSize(X_HIT, X_HIT)
    pill.removeBtn:SetPoint("LEFT", pill.name, "RIGHT", GAP_INNER, 0)

    pill.removeBtn.tex = pill.removeBtn:CreateTexture(nil, "ARTWORK")
    pill.removeBtn.tex:SetSize(X_SIZE, X_SIZE)
    pill.removeBtn.tex:SetPoint("CENTER")
    pill.removeBtn.tex:SetTexture(Theme.GetIconPath("x"))
    pill.removeBtn.tex:SetVertexColor(Theme.TEXT_DIM.r, Theme.TEXT_DIM.g, Theme.TEXT_DIM.b, Theme.TEXT_DIM.a)
    pill.removeBtn.tex:SetSnapToPixelGrid(false)
    pill.removeBtn.tex:SetTexelSnappingBias(0)

    pill.removeBtn:SetScript("OnEnter", function()
        pill.removeBtn.tex:SetVertexColor(Theme.DANGER.r, Theme.DANGER.g, Theme.DANGER.b, Theme.DANGER.a)
    end)
    pill.removeBtn:SetScript("OnLeave", function()
        pill.removeBtn.tex:SetVertexColor(Theme.TEXT_DIM.r, Theme.TEXT_DIM.g, Theme.TEXT_DIM.b, Theme.TEXT_DIM.a)
    end)
    pill.removeBtn:SetScript("OnClick", function() onRemove() end)

    -- Size the pill to fit its content: leftPad + icon + gap + name + gap + X + rightPad.
    local width = PAD_LEFT + Theme.ICON_SM + GAP_INNER + pill.name:GetStringWidth()
        + GAP_INNER + X_HIT + PAD_RIGHT
    pill:SetWidth(width)

    -- Real in-game item tooltip once the client can resolve the item, mirroring
    -- CreateCooldownPill's GameTooltip:SetSpellByID above. Resolution is re-checked
    -- on every hover rather than once here: an item the client has not cached yet
    -- resolves a few frames later, and deciding at creation time would leave the
    -- pill on the plain-text fallback for its whole lifetime.
    --
    -- WTUI:AddTooltip below drives a SEPARATE tooltip frame (WTUI.Tooltip) off an
    -- OnEnter *hook*, so it runs after this handler and would stack a second tip on
    -- top of the item one. Its handler no-ops on an empty tooltipText, so the two
    -- are made mutually exclusive by moving that text in and out per hover.
    -- AddTooltip must stay AFTER this SetScript -- a SetScript replaces hooks.
    pill:SetScript("OnEnter", function()
        -- An equipped item's real item level lives in the bonusIDs of its LINK, which
        -- a bare itemID does not carry: SetItemByID renders the base variant (a
        -- trinket worn at ilvl 298 shows as 28). So when the caller declared which
        -- slots this item can occupy, read the tooltip off the live slot instead --
        -- that is the only form that carries the equipped upgrade level. Re-resolved
        -- per hover, so re-equipping is reflected without a rebuild.
        local slot = FindEquippedSlot(itemID, equipSlots)

        if slot or C_Item.GetItemInfo(itemID) then
            pill.tooltipText = ""
            GameTooltip:SetOwner(pill, "ANCHOR_RIGHT")
            if slot then
                GameTooltip:SetInventoryItem("player", slot)
            else
                -- Not currently worn (a greyed pill): the base variant is all that is
                -- knowable from an itemID alone, and is better than no tooltip.
                GameTooltip:SetItemByID(itemID)
            end
            GameTooltip:Show()
        else
            pill.tooltipText = itemName
        end
    end)
    pill:SetScript("OnLeave", function()
        GameTooltip:Hide()
    end)

    WTUI:AddTooltip(pill, itemName)

    return pill
end
