-- DopingControl data/consumables.lua
-- Consumables tab: slot definitions + buff->entry table + spellID->slot map
-- + the pure item-identification ladder (DC.ResolveItem).
-- Pure Lua 5.0, no WoW API -- dofile-loadable offline.
--
-- SLOT MODEL (v6, 22 slots -- the 17-slot base design plus the five school
-- protection-potion columns FrR/NR/SR/HR/AR added 2026-08-11):
-- One column = one thing that can be active independently of every other
-- column; items competing INSIDE a column are the "pick one" alternatives.
-- Source: community-sourced TurtleWoW 1.17.2 consumable-stacking rules,
-- trust external -- vanilla 1.12 slot rules, no TBC
-- Battle/Guardian categories. The old 11-slot model conflated four
-- independent slots into SP (GAE / school elixir / Dreamtonic / Dreamshard
-- are simultaneously active in the caster set) and two into HP (Fortitude
-- + stamina alcohol stack), and lacked BL/ZANZA entirely.
--
-- Deliberate compromises (documented, not hidden):
--   * ALC is ONE column although different alcohol types stack (Merlot +
--     Rumsey Black Label at once; same types do not): modelled as one
--     column showing the strongest active, detail in tooltip. The
--     slot-collision tests exempt exactly this column.
--   * Gift of Arthas and Juju Flurry get NO column (defense elixirs all
--     stack; Flurry lasts 20 s).
--   * UNVERIFIED gaps (community source does not cover them; marked
--     `unverified = true` on the slot/entries, upgrade via dev/raiddump
--     evidence next raid): (1) WPN main-hand/off-hand coexistence (oil on
--     MH + stone on OH), (2) protection-potion slot rules (all six school
--     columns FR/FrR/NR/SR/HR/AR -- cross-school stacking is NOT
--     evidenced, so each school gets its own column but every one stays
--     flagged unverified), (3) world-buff interactions (no world-buff
--     columns at all).
--
-- Entry fields (DC.CONSUMABLES[buffName]):
--   slot     : slot id (column) the buff fills
--   spellID  : aura spell id; 0 = "match by name only" (no known ID)
--   item     : the consumable's display name (whisper/tooltip/cell)
--   icon     : BARE icon texture name (no "Interface\Icons\" prefix, no
--              extension -- prefix is added at render time); nil when the
--              icon could not be sourced (the matrix fallback ladder
--              handles nil: slot-generic icon, then the green check)
--   discrim  : optional Lua 5.0 string.find pattern matched against the
--              buff tooltip's EFFECT line (scan/aura.lua auras.effects) --
--              only where the buff NAME is generic
--   variants : optional ARRAY of { item, icon, discrim } for generic buff
--              names ("Well Fed") that many different items produce; the
--              identification ladder walks them in order, FIRST match wins
--   unverified : optional true -- see the honesty gaps above
--
-- Icon provenance (2026-08-11, never guessed): wowhead classic tooltip
-- API for the vanilla items, octowow.st/db + wowauctions for TurtleWoW
-- customs; icon = nil where no source confirmed one. Two lookalike
-- collisions are GENUINE, not typos: Dragonbreath Chili and Danonzo's
-- Delight share inv_drink_17 with Nightfin Soup; Gordok Green Grog
-- shares inv_drink_03 with Rumsey Rum; Cerebral Cortex Compound shares
-- inv_potion_32 with the Mongoose elixir.
--
-- Separator decision (once for all data files): items strings are
-- tooltip/FontString-only, so they may use the middle-dot list separator
-- "\194\183" (UTF-8 middle dot renders fine in own FontStrings; the group
-- headers already use the same glyph). Em dashes stay ASCII "-" (that
-- glyph is unproven in the 1.12 font). SENT chat lines remain pure ASCII
-- -- they never use these strings.

DopingControl = DopingControl or {}
local DC = DopingControl

-- ------------------------------------------------------------------
-- Slot definitions (array, order = column order).
-- Fields: id (stable key, used by DEFAULT_EXPECT / model / UI),
--         label (column tile abbrev), sub (tile sub-label), full (long
--         name, used in whisper/report), kind ("aura" | "weapon"),
--         items (tooltip example text, optional), unverified (optional,
--         see header),
--         icon (BARE slot-generic icon texture name -- same bare-name
--         rule as the entry icons above; the matrix HAS-cell fallback
--         ladder renders it when the ITEM behind a buff cannot be
--         resolved: resolved item icon -> this slot icon -> green check.
--         A representative member item's icon by design -- the cell must
--         read as "this column", not as a question mark).
-- ------------------------------------------------------------------
DC.SLOTS_CONSUMABLES = {
    { id = "FLASK",  label = "FLK", sub = "Flask",   full = "Flask",              kind = "aura",
      icon = "inv_potion_62",
      items = "Flask of the Titans \194\183 Distilled Wisdom \194\183 Supreme Power" },
    { id = "FOOD",   label = "FOD", sub = "Food",    full = "Food buff",          kind = "aura",
      icon = "inv_misc_food_15",
      items = "one food buff at a time - e.g. Smoked Desert Dumplings \194\183 Danonzo's Tel'Abim foods" },
    { id = "AP",     label = "AP",  sub = "Attack",  full = "Attack power",       kind = "aura",
      icon = "inv_potion_92",
      items = "Winterfall Firewater \194\183 Juju Might" },
    { id = "STR",    label = "STR", sub = "Str.",    full = "Strength",           kind = "aura",
      icon = "inv_potion_61",
      items = "Juju Power \194\183 Elixir of Giants" },
    { id = "AGI",    label = "AGI", sub = "Agi.",    full = "Agility",            kind = "aura",
      icon = "inv_potion_32",
      items = "Elixir of the Mongoose - free-stacker, own column" },
    { id = "BL",     label = "BL",  sub = "Blasted", full = "Blasted Lands buff", kind = "aura",
      icon = "inv_stone_15",
      items = "R.O.I.D.S. \194\183 Ground Scorpok Assay \194\183 Lung Juice Cocktail \194\183 Cortex Compound \194\183 Gizzard Gum" },
    { id = "ZANZA",  label = "ZAN", sub = "Zanza",   full = "Spirit of Zanza",    kind = "aura",
      icon = "inv_potion_30",
      items = "Spirit of Zanza - free-stacker, one Zanza effect at a time" },
    { id = "GAE",    label = "GAE", sub = "Arcane",  full = "Greater Arcane Elixir", kind = "aura",
      icon = "inv_potion_25",
      items = "Greater Arcane Elixir \194\183 Arcane Elixir" },
    { id = "SCHOOL", label = "SCH", sub = "School",  full = "School elixir",      kind = "aura",
      icon = "inv_potion_46",
      items = "Shadow Power \194\183 Frost Power \194\183 Greater Firepower - one per school slot" },
    { id = "DREAMT", label = "DRT", sub = "Dreamt.", full = "Dreamtonic",         kind = "aura",
      icon = "inv_potion_10",
      items = "Dreamtonic (TurtleWoW custom)" },
    { id = "SHARD",  label = "SHD", sub = "Shard",   full = "Dreamshard Elixir",  kind = "aura",
      icon = "inv_potion_12",
      items = "Dreamshard Elixir (TurtleWoW custom)" },
    { id = "MP5",    label = "MP5", sub = "Mana",    full = "Mana regen",         kind = "aura",
      icon = "inv_potion_45",
      items = "Mageblood Potion - Nightfin Soup is a FOOD buff, not this column" },
    { id = "ARM",    label = "ARM", sub = "Armor",   full = "Armor",              kind = "aura",
      icon = "inv_potion_66",
      items = "Elixir of Superior Defense" },
    { id = "HPELX",  label = "FRT", sub = "Fort.",   full = "Elixir of Fortitude", kind = "aura",
      icon = "inv_potion_43",
      items = "Elixir of Fortitude - stacks with the alcohol column" },
    { id = "ALC",    label = "ALC", sub = "Alcohol", full = "Alcohol buff",       kind = "aura",
      icon = "inv_drink_04",
      items = "Medivh's Merlot \194\183 Rumsey Rum Black Label \194\183 Kreeg's Stout Beatdown - different types stack, same type does not; one column, strongest shown" },
    { id = "FR",     label = "FR",  sub = "fire",    full = "Fire protection",    kind = "aura",  unverified = true,
      icon = "inv_potion_24",
      items = "Greater Fire Protection Potion - situational per boss; enable/disable via the expectation matrix" },
    -- The remaining school protection columns follow the FR pattern (one
    -- column per school, cross-school stacking unverified -- see header).
    -- Unlike FR they carry NO seed entry in data/expectations.lua: the
    -- dynamic-column rule keeps them hidden until enabled per role/class.
    { id = "FrR",    label = "FrR", sub = "frost",   full = "Frost protection",   kind = "aura",  unverified = true,
      icon = "inv_potion_20",
      items = "Greater Frost Protection Potion - situational per boss; enable/disable via the expectation matrix" },
    { id = "NR",     label = "NR",  sub = "nature",  full = "Nature protection",  kind = "aura",  unverified = true,
      icon = "inv_potion_22",
      items = "Greater Nature Protection Potion - situational per boss; enable/disable via the expectation matrix" },
    { id = "SR",     label = "SR",  sub = "shadow",  full = "Shadow protection",  kind = "aura",  unverified = true,
      icon = "inv_potion_23",
      items = "Greater Shadow Protection Potion - situational per boss; enable/disable via the expectation matrix" },
    { id = "HR",     label = "HR",  sub = "holy",    full = "Holy protection",    kind = "aura",  unverified = true,
      icon = "inv_potion_09",
      items = "Holy Protection Potion - situational per boss; enable/disable via the expectation matrix" },
    { id = "AR",     label = "AR",  sub = "arcane",  full = "Arcane protection",  kind = "aura",  unverified = true,
      icon = "inv_potion_83",
      items = "Greater Arcane Protection Potion - situational per boss; enable/disable via the expectation matrix" },
    { id = "WPN",    label = "WPN", sub = "temp.",   full = "Weapon (temporary)", kind = "weapon", unverified = true,  -- NOT an aura slot; MH/OH split unverified
      icon = "inv_stone_sharpeningstone_01",
      items = "Elemental/Dense Sharpening Stone \194\183 Brilliant Wizard/Mana Oil - permanent enchant: Equipment tab" },
}

-- ------------------------------------------------------------------
-- Buff name -> entry table.
-- spellID = 0 means "match by name only" (no known aura spell ID).
-- ------------------------------------------------------------------
DC.CONSUMABLES = {
    -- FLASK
    ["Flask of the Titans"]           = { slot = "FLASK", spellID = 17626, item = "Flask of the Titans",           icon = "inv_potion_62" },
    ["Flask of Supreme Power"]        = { slot = "FLASK", spellID = 17628, item = "Flask of Supreme Power",        icon = "inv_potion_41" },
    ["Flask of Distilled Wisdom"]     = { slot = "FLASK", spellID = 17627, item = "Flask of Distilled Wisdom",     icon = "inv_potion_97" },
    ["Flask of Chromatic Resistance"] = { slot = "FLASK", spellID = 17629, item = "Flask of Chromatic Resistance", icon = "inv_potion_48" },
    ["Flask of Petrification"]        = { slot = "FLASK", spellID = 17624, item = "Flask of Petrification",        icon = "inv_potion_26" },  -- NOT inv_potion_98 (a often-repeated wrong guess) -- wowhead-confirmed

    -- FOOD (buff names, not item names).
    -- "Well Fed" is the generic food aura: the ITEM is identified through
    -- the tooltip EFFECT line (scan/aura.lua auras.effects) against the
    -- per-variant discrim patterns below -- first match wins, no match =
    -- nil = the matrix falls back to the slot-generic icon ("food buff,
    -- unknown which"). The variant list is the food set of the KG
    -- stacking-rules atom plus the classic raid staples; the discrim
    -- patterns are best-effort data (recorded-tooltip verification via
    -- dev/raiddump pending) -- a wrong one is a one-line fix.
    -- ["Food"] (the eating tick) deliberately REMOVED: it is the
    -- sit-and-eat channel aura, not a food buff -- it made the FOOD cell
    -- green for anyone currently chewing (field-tested). Only lasting
    -- food buffs count.
    ["Well Fed"] = { slot = "FOOD", spellID = 0, item = "food buff", icon = nil,
        variants = {
            -- ORDER MATTERS: first match wins, so specific effect lines
            -- (crit, ranged AP, spell damage) sit ABOVE the generic stat
            -- words they could otherwise be swallowed by.
            -- Several Turtle customs SHARE their aura spell with a classic
            -- staple (icon research 2026-08-11: Power Mushroom = 24800 =
            -- Dumplings, Sour Mountain Berry = 18230 = Grilled Squid,
            -- Juicy Striped Melon = 22731 = Runn Tum Tuber) -- those pairs
            -- are indistinguishable by effect line; the listed-first item
            -- is shown as the representative. Same slot, so harmless.
            { item = "Danonzo's Tel'Abim Delight",    icon = "inv_drink_17",         discrim = "[Ss]pell [Dd]amage" },
            { item = "Danonzo's Tel'Abim Medley",     icon = "inv_misc_food_08",     discrim = "[Hh]aste" },
            { item = "Danonzo's Tel'Abim Surprise",   icon = "inv_misc_food_09",     discrim = "[Rr]anged [Aa]ttack [Pp]ower" },
            { item = "Gurubashi Gumbo",               icon = "inv_misc_food_64",     discrim = "[Cc]rit" },
            { item = "Le Fishe Au Chocolat",          icon = nil,                    discrim = "[Dd]odge" },  -- icon: questionmark placeholder even upstream (octowow + wowauctions), stays nil
            { item = "Empowering Herbal Salad",       icon = "inv_misc_food_salad",  discrim = "[Hh]ealing" },
            { item = "Nightfin Soup",                 icon = "inv_drink_17",         discrim = "8 [Mm]ana" },
            { item = "Sagefish Delight",              icon = "inv_misc_fish_21",     discrim = "6 [Mm]ana" },
            -- +20 Strength: Dumplings and Power Mushroom, same spell 24800
            { item = "Smoked Desert Dumplings",       icon = "inv_misc_food_64",     discrim = "[Ss]trength" },
            { item = "Power Mushroom",                icon = "inv_mushroom_11",      discrim = "[Ss]trength" },  -- NOT "+10 all stats" (dead pattern until 2026-08-11); tooltip-verified +20 STR
            -- +10 Agility: Grilled Squid and Sour Mountain Berry, same spell 18230
            { item = "Grilled Squid",                 icon = "inv_misc_fish_13",     discrim = "[Aa]gility" },
            { item = "Sour Mountain Berry",           icon = "inv_misc_food_40",     discrim = "[Aa]gility" },
            -- +10 Intellect: classic Tuber and Turtle Melon, same spell 22731
            { item = "Runn Tum Tuber Surprise",       icon = "inv_misc_food_63",     discrim = "[Ii]ntellect" },
            { item = "Juicy Striped Melon",           icon = "inv_misc_food_22",     discrim = "[Ii]ntellect" },
            -- generic stamina LAST: Gumbo's line also contains Stamina and
            -- must be caught by its crit pattern above, never by this one
            { item = "Hardened Mushroom",             icon = "inv_mushroom_11",      discrim = "[Ss]tamina" },  -- NOT "+armor" (dead pattern until 2026-08-11); tooltip-verified +25 STA, spell 25660
        } },
    ["Increased Stamina"]             = { slot = "FOOD", spellID = 0,     item = "stamina food",       icon = nil },
    ["Dragonbreath Chili"]            = { slot = "FOOD", spellID = 15852, item = "Dragonbreath Chili", icon = "inv_drink_17" },  -- yes, the chili really uses a drink icon (wowhead-confirmed via og:image)

    -- AP
    ["Winterfall Firewater"]          = { slot = "AP", spellID = 17038, item = "Winterfall Firewater", icon = "inv_potion_92" },
    ["Juju Might"]                    = { slot = "AP", spellID = 16329, item = "Juju Might",           icon = "inv_misc_monsterscales_07" },

    -- STR
    ["Juju Power"]                    = { slot = "STR", spellID = 16323, item = "Juju Power",            icon = "inv_misc_monsterscales_11" },
    ["Elixir of Giants"]              = { slot = "STR", spellID = 11405, item = "Elixir of Giants",      icon = "inv_potion_61" },
    ["Elixir of the Giants"]          = { slot = "STR", spellID = 11405, item = "Elixir of Giants",      icon = "inv_potion_61" },  -- AURA name on this client (measured live: SpellInfo(11405) = "Elixir of the Giants"); the item is named without "the", and matching runs on the aura name first
    ["Elixir of Brute Force"]         = { slot = "STR", spellID = 17537, item = "Elixir of Brute Force", icon = "inv_potion_40" },

    -- AGI
    ["Elixir of the Mongoose"]        = { slot = "AGI", spellID = 17538, item = "Elixir of the Mongoose",    icon = "inv_potion_32" },
    ["Elixir of Greater Agility"]     = { slot = "AGI", spellID = 11334, item = "Elixir of Greater Agility", icon = "inv_potion_94" },
    ["Greater Agility"]               = { slot = "AGI", spellID = 11334, item = "Elixir of Greater Agility", icon = "inv_potion_94" },  -- AURA name (the item name never appears as a buff); was tallied as unknown 14x
    ["Elixir of Agility"]             = { slot = "AGI", spellID = 11328, item = "Elixir of Agility",         icon = "inv_potion_93" },

    -- BL (Blasted Lands buff slot -- one at a time, pick one; the five
    -- items were split across STR/nowhere in the old model).
    -- Aura names + spell ids wowhead-confirmed 2026-08-11 ("Strike of the
    -- Scorpok" additionally measured live in a 26-man raid). The item
    -- names ride along as alias keys (spellID 0) because the Turtle
    -- client may surface the permanent aura under the item name (KG
    -- note); a key that never matches is harmless.
    ["Rage of Ages"]                  = { slot = "BL", spellID = 10667, item = "R.O.I.D.S.",               icon = "inv_stone_15" },
    ["R.O.I.D.S."]                    = { slot = "BL", spellID = 0,     item = "R.O.I.D.S.",               icon = "inv_stone_15" },
    ["Strike of the Scorpok"]         = { slot = "BL", spellID = 10669, item = "Ground Scorpok Assay",     icon = "inv_misc_dust_02" },  -- old model filed it under STR with the aura name as item -- both wrong (it is +25 AGI, and the item is the Assay)
    ["Ground Scorpok Assay"]          = { slot = "BL", spellID = 0,     item = "Ground Scorpok Assay",     icon = "inv_misc_dust_02" },
    ["Spirit of Boar"]                = { slot = "BL", spellID = 10668, item = "Lung Juice Cocktail",      icon = "inv_drink_12" },
    ["Lung Juice Cocktail"]           = { slot = "BL", spellID = 0,     item = "Lung Juice Cocktail",      icon = "inv_drink_12" },
    ["Infallible Mind"]               = { slot = "BL", spellID = 10692, item = "Cerebral Cortex Compound", icon = "inv_potion_32" },  -- same icon as the Mongoose elixir -- genuine, wowhead-confirmed
    ["Cerebral Cortex Compound"]      = { slot = "BL", spellID = 0,     item = "Cerebral Cortex Compound", icon = "inv_potion_32" },
    ["Spiritual Domination"]          = { slot = "BL", spellID = 10693, item = "Gizzard Gum",              icon = "inv_misc_food_30" },
    ["Gizzard Gum"]                   = { slot = "BL", spellID = 0,     item = "Gizzard Gum",              icon = "inv_misc_food_30" },

    -- ZANZA (free-stacker; only one Zanza-potion effect at a time; buff
    -- name = item name, wowhead-confirmed)
    ["Spirit of Zanza"]               = { slot = "ZANZA", spellID = 24382, item = "Spirit of Zanza", icon = "inv_potion_30" },

    -- GAE (split out of the old SP column)
    ["Greater Arcane Elixir"]         = { slot = "GAE", spellID = 17539, item = "Greater Arcane Elixir", icon = "inv_potion_25" },
    ["Arcane Elixir"]                 = { slot = "GAE", spellID = 11390, item = "Arcane Elixir",         icon = "inv_potion_30" },

    -- SCHOOL (split out of the old SP column; one school elixir at a time)
    ["Elixir of Shadow Power"]        = { slot = "SCHOOL", spellID = 11474, item = "Elixir of Shadow Power",        icon = "inv_potion_46" },
    ["Elixir of Frost Power"]         = { slot = "SCHOOL", spellID = 21920, item = "Elixir of Frost Power",         icon = "inv_potion_03" },
    ["Greater Frost Power"]           = { slot = "SCHOOL", spellID = 56544, item = "Elixir of Greater Frost Power", icon = "inv_potion_13" },  -- AURA name (TurtleWoW-added). NOT measured in-game: taken from a TurtleWoW spell database (56544 = "Greater Frost Power") plus the item name in pfQuest-turtle's item DB; the aura drops "Elixir of" the same way Giants/Agility above do
    ["Elixir of Greater Firepower"]   = { slot = "SCHOOL", spellID = 26276, item = "Elixir of Greater Firepower",   icon = "inv_potion_60" },
    ["Elixir of Firepower"]           = { slot = "SCHOOL", spellID = 7844,  item = "Elixir of Firepower",           icon = "inv_potion_33" },

    -- DREAMT (TurtleWoW custom; own slot -- simultaneously active with
    -- GAE, SCHOOL and SHARD in the caster set)
    ["Dreamtonic"]                    = { slot = "DREAMT", spellID = 45489, item = "Dreamtonic", icon = "inv_potion_10" },  -- use-spell ID; the aura's own ID is unverified

    -- SHARD (TurtleWoW custom; own slot)
    ["Dreamshard Elixir"]             = { slot = "SHARD", spellID = 45427, item = "Dreamshard Elixir", icon = "inv_potion_12" },  -- use-spell ID; the aura's own ID is unverified

    -- MP5 (narrowed: Nightfin Soup is a FOOD buff and lives there now)
    ["Mageblood Potion"]              = { slot = "MP5", spellID = 24363, item = "Mageblood Potion", icon = "inv_potion_45" },
    ["Mageblood"]                     = { slot = "MP5", spellID = 24363, item = "Mageblood Potion", icon = "inv_potion_45" },  -- TWoW buff-name variant (old Data.lua kept both)

    -- ARM
    ["Elixir of Superior Defense"]    = { slot = "ARM", spellID = 11348, item = "Elixir of Superior Defense", icon = "inv_potion_66" },
    ["Elixir of Greater Defense"]     = { slot = "ARM", spellID = 11349, item = "Elixir of Greater Defense",  icon = "inv_potion_65" },

    -- HPELX (alcohol split out into ALC; Fortitude stacks with it)
    ["Elixir of Fortitude"]           = { slot = "HPELX", spellID = 3593, item = "Elixir of Fortitude", icon = "inv_potion_43" },
    ["Health II"]                     = { slot = "HPELX", spellID = 3593, item = "Elixir of Fortitude", icon = "inv_potion_43" },  -- AURA name on this client (measured live: SpellInfo(3593) = "Health II")

    -- ALC (alcohol buffs -- ONE column by design although different
    -- types stack; see the header compromise note). Buff names and spell
    -- ids wowhead/octowow-confirmed 2026-08-11; the "Blue Label" aura
    -- name differs from the Blue item name, both keys are kept.
    ["Medivh's Merlot"]               = { slot = "ALC", spellID = 57106, item = "Medivh's Merlot",        icon = "inv_drink_waterskin_05" },  -- use-spell ID; the aura's own ID is unverified
    ["Medivh's Merlot Blue"]          = { slot = "ALC", spellID = 0,     item = "Medivh's Merlot Blue",   icon = "inv_drink_waterskin_01" },
    ["Medivh's Merlot Blue Label"]    = { slot = "ALC", spellID = 57107, item = "Medivh's Merlot Blue",   icon = "inv_drink_waterskin_01" },  -- the AURA name (octowow: spell 57107 "Medivh's Merlot Blue Label")
    ["Rumsey Rum Black Label"]        = { slot = "ALC", spellID = 25804, item = "Rumsey Rum Black Label", icon = "inv_drink_04" },
    ["Rumsey Rum"]                    = { slot = "ALC", spellID = 20875, item = "Rumsey Rum",             icon = "inv_drink_03" },
    ["Rumsey Rum Light"]              = { slot = "ALC", spellID = 25037, item = "Rumsey Rum Light",       icon = "inv_drink_08" },
    ["Gordok Green Grog"]             = { slot = "ALC", spellID = 22789, item = "Gordok Green Grog",      icon = "inv_drink_03" },  -- same icon as Rumsey Rum -- genuine, wowhead-confirmed
    ["Kreeg's Stout Beatdown"]        = { slot = "ALC", spellID = 22790, item = "Kreeg's Stout Beatdown", icon = "inv_drink_05" },  -- +25 Spirit/-5 Int -- the healer set's alcohol, not a stamina drink

    -- FR (protection-potion slot rules are an UNVERIFIED gap, see header)
    ["Greater Fire Protection"]       = { slot = "FR", spellID = 17543, item = "Greater Fire Protection Potion", icon = "inv_potion_24", unverified = true },
    ["Fire Protection"]               = { slot = "FR", spellID = 7233,  item = "Fire Protection Potion",         icon = "inv_potion_16", unverified = true },

    -- FrR/NR/SR/HR/AR (same UNVERIFIED protection-potion gap as FR).
    -- Use-effect spell IDs + item icons wowhead-confirmed 2026-08-11.
    -- Tier coverage is asymmetric by design: no Lesser Arcane potion and
    -- no Greater Holy potion exist in vanilla 1.12. The aura on the
    -- client likely drops the "Greater" (wowhead names both tiers
    -- "<School> Protection"); the greater keys mirror the FR convention
    -- and the ID match carries them regardless of the live aura name.
    ["Greater Frost Protection"]      = { slot = "FrR", spellID = 17544, item = "Greater Frost Protection Potion",  icon = "inv_potion_20", unverified = true },
    ["Frost Protection"]              = { slot = "FrR", spellID = 7239,  item = "Frost Protection Potion",          icon = "inv_potion_13", unverified = true },
    ["Greater Nature Protection"]     = { slot = "NR",  spellID = 17546, item = "Greater Nature Protection Potion", icon = "inv_potion_22", unverified = true },
    ["Nature Protection"]             = { slot = "NR",  spellID = 7254,  item = "Nature Protection Potion",         icon = "inv_potion_06", unverified = true },  -- live aura name carries a trailing space (SpellInfo(7254)); scan/aura.lua's trimmed retry covers it
    ["Greater Shadow Protection"]     = { slot = "SR",  spellID = 17548, item = "Greater Shadow Protection Potion", icon = "inv_potion_23", unverified = true },
    ["Shadow Protection"]             = { slot = "SR",  spellID = 7242,  item = "Shadow Protection Potion",         icon = "inv_potion_44", unverified = true },  -- NAME collides with the priest buff (data/classbuffs.lua SPROT, ids 976/10957/10958): ID-before-name keeps them apart; a name-only scan cannot tell the two, which SR's default-off expectation mitigates
    ["Holy Protection"]               = { slot = "HR",  spellID = 7245,  item = "Holy Protection Potion",           icon = "inv_potion_09", unverified = true },
    ["Greater Arcane Protection"]     = { slot = "AR",  spellID = 17549, item = "Greater Arcane Protection Potion", icon = "inv_potion_83", unverified = true },

    -- WPN: intentionally NO aura entries - temp enchant is not a buff
}

-- ------------------------------------------------------------------
-- DC.ResolveItem(buffName, effectText) -> item, icon
-- The PURE identification ladder (spec: "Identification ladder"):
--   1. unique buff name          -> item/icon straight from the entry
--   2. generic buff name         -> match effectText against the entry's
--      variant discrim patterns (plain Lua 5.0 string.find, first match
--      wins) -> that variant's item/icon
--   3. no match / no effect text -> nil, nil (the caller falls back to
--      the slot-generic icon: "slot filled, item unknown")
-- An entry-level discrim (no variants) gates rule 1 the same way: the
-- item only resolves when the effect line matches. Offline-testable --
-- no WoW API, no upvalues beyond the data table.
-- ------------------------------------------------------------------
function DC.ResolveItem(buffName, effectText)
    local def = DC.CONSUMABLES[buffName]
    if not def then
        return nil, nil
    end
    if def.variants then
        if type(effectText) == "string" then
            local n = table.getn(def.variants)
            for i = 1, n do
                local v = def.variants[i]
                if v.discrim and string.find(effectText, v.discrim) then
                    return v.item, v.icon
                end
            end
        end
        return nil, nil
    end
    if def.discrim then
        if type(effectText) == "string"
           and string.find(effectText, def.discrim) then
            return def.item, def.icon
        end
        return nil, nil
    end
    return def.item, def.icon
end

-- ------------------------------------------------------------------
-- spellID -> slotId map, generated from DC.CONSUMABLES (spellID > 0).
-- Collision rule: at classify time an ID match beats a
-- name match. data/classbuffs.lua later MERGES its rank IDs into this same
-- map (slot ids are unique across tabs, so one flat map is safe).
-- ------------------------------------------------------------------
DC.SPELL_TO_SLOT = {}
for name, def in pairs(DC.CONSUMABLES) do
    if def.spellID and def.spellID > 0 then
        DC.SPELL_TO_SLOT[def.spellID] = def.slot
    end
end
