-- MRB.lua — Shaman Resource Bar v1
-- Single status bar for both Enhancement (0-10 stacks) and Elemental (0-100 power).

local _, playerClass = UnitClass("player")
if playerClass ~= "SHAMAN" then return end

-- ── LibSharedMedia ────────────────────────────────────────────────────
local LSM = LibStub and LibStub("LibSharedMedia-3.0", true)
local function GetBarTexture(name)
    if LSM then
        return LSM:Fetch(LSM.MediaType.STATUSBAR, name)
            or "Interface\\TargetingFrame\\UI-StatusBar"
    end
    return "Interface\\TargetingFrame\\UI-StatusBar"
end

-- ── Constants ────────────────────────────────────────────────────────
local ENHANCEMENT_SPEC  = 263
local ELEMENTAL_SPEC    = 262
local RESTORATION_SPEC  = 264
local MSW_SPELL_ID      = 344179
local MAELSTROM_POWER   = (Enum and Enum.PowerType and Enum.PowerType.Maelstrom) or 11
local MANA_POWER        = (Enum and Enum.PowerType and Enum.PowerType.Mana)      or 0

-- Sound definitions: built-in WoW SoundKitIDs and custom addon files.
-- { label, id }    → PlaySound(id, "Master")
-- { label, file }  → PlaySoundFile("Interface\\AddOns\\...\\Sounds\\file", "Master")
local ADDON_SOUND_PATH = "Interface\\AddOns\\Shaman_Resource_Bar_v1\\Sounds\\"

local THRESHOLD_SOUNDS = {
    -- ── Built-in WoW sounds ──────────────────────────────────────────
    { label = "-- None --" },
    { label = "Ping",          id = 600    },
    { label = "Ready Check",   id = 8356   },
    { label = "Quest Done",    id = 878    },
    { label = "Level Up",      id = 888    },
    { label = "Raid Warning",  id = 8959   },
    { label = "Power Aura",    id = 569593 },
    -- ── Custom addon sounds ──────────────────────────────────────────
    { label = "Acoustic Guitar",        file = "AcousticGuitar.ogg"        },
    { label = "Air Horn",               file = "AirHorn.ogg"               },
    { label = "Applause",               file = "Applause.ogg"              },
    { label = "Banana Peel Slip",       file = "BananaPeelSlip.ogg"        },
    { label = "Batman Punch",           file = "BatmanPunch.ogg"           },
    { label = "Bike Horn",              file = "BikeHorn.ogg"              },
    { label = "Blast",                  file = "Blast.ogg"                 },
    { label = "Bleat",                  file = "Bleat.ogg"                 },
    { label = "Boxing Arena",           file = "BoxingArenaSound.ogg"      },
    { label = "Brass",                  file = "Brass.mp3"                 },
    { label = "Cartoon Voice Baritone", file = "CartoonVoiceBaritone.ogg"  },
    { label = "Cartoon Walking",        file = "CartoonWalking.ogg"        },
    { label = "Cat Meow",               file = "CatMeow2.ogg"              },
    { label = "Chicken Alarm",          file = "ChickenAlarm.ogg"          },
    { label = "Cow Mooing",             file = "CowMooing.ogg"             },
    { label = "Double Whoosh",          file = "DoubleWhoosh.ogg"          },
    { label = "Drums",                  file = "Drums.ogg"                 },
    { label = "Electrical Spark",       file = "ElectricalSpark.ogg"       },
    { label = "Error Beep",             file = "ErrorBeep.ogg"             },
    { label = "Glass",                  file = "Glass.mp3"                 },
    { label = "Goat Bleating",          file = "GoatBleating.ogg"          },
    { label = "Heartbeat",              file = "HeartbeatSingle.ogg"       },
    { label = "Kitten Meow",            file = "KittenMeow.ogg"            },
    { label = "Oh No",                  file = "OhNo.ogg"                  },
    { label = "Ringing Phone",          file = "RingingPhone.ogg"          },
    { label = "Roaring Lion",           file = "RoaringLion.ogg"           },
    { label = "Robot Blip",             file = "RobotBlip.ogg"             },
    { label = "Rooster",                file = "RoosterChickenCalls.ogg"   },
    { label = "Sharp Punch",            file = "SharpPunch.ogg"            },
    { label = "Sheep Bleat",            file = "SheepBleat.ogg"            },
    { label = "Shotgun",                file = "Shotgun.ogg"               },
    { label = "Squeaky Toy",            file = "SqueakyToyShort.ogg"       },
    { label = "Squish Fart",            file = "SquishFart.ogg"            },
    { label = "Synth Chord",            file = "SynthChord.ogg"            },
    { label = "Tada Fanfare",           file = "TadaFanfare.ogg"           },
    { label = "Temple Bell",            file = "TempleBellHuge.ogg"        },
    { label = "Thunder",                file = "Thunder.ogg"               },
    { label = "Torch",                  file = "Torch.ogg"                 },
    { label = "Warning Siren",          file = "WarningSiren.ogg"          },
    { label = "Water Drop",             file = "WaterDrop.ogg"             },
    { label = "Xylophone",              file = "Xylophone.ogg"             },
}

local THRESHOLD_SOUND_MAP  = {}  -- label → entry
local THRESHOLD_SOUND_LIST = {}  -- label → label  (for AceConfig select)
for _, s in ipairs(THRESHOLD_SOUNDS) do
    THRESHOLD_SOUND_MAP[s.label]  = s
    THRESHOLD_SOUND_LIST[s.label] = s.label
end

local function PlayThresholdSound(soundLabel)
    local s = THRESHOLD_SOUND_MAP[soundLabel]
    if not s then return end
    if s.file then
        PlaySoundFile(ADDON_SOUND_PATH .. s.file, "Master")
    elseif s.id then
        PlaySound(s.id, "Master")
    end
end

-- ── DB defaults ───────────────────────────────────────────────────────
local function DefaultThreshold(stacks, r, g, b)
    return { enabled=false, stacks=stacks, color={r=r,g=g,b=b,a=1}, sound="-- None --" }
end

local DB_DEFAULTS = {
    profile = {
        width           = 200,
        height          = 24,
        locked          = false,
        hideOutOfCombat = false,
        hideMounted     = false,
        hideVehicle     = false,
        hidePetBattle   = false,
        posX            = nil,
        posY            = nil,
        enhColor        = { r=1,   g=0.5, b=0,   a=1 },
        elemColor       = { r=0,   g=0.6, b=1,   a=1 },
        restColor       = { r=0.2, g=0.8, b=0.4, a=1 },  -- soft green for mana
        bgColor         = { r=0,   g=0,   b=0,   a=0.6 },
        border          = { show=true, thickness=1, color={r=0,g=0,b=0,a=1} },
        barTexture      = "Blizzard",

        -- Per-spec label settings
        labelSpecs      = {
            enhancement = {
                show=true, format="compact", size=12,
                font="Friz Quadrata TT", color={r=1,g=1,b=1,a=1},
                offsetX=0, offsetY=0,
            },
            elemental = {
                show=true, format="compact", size=12,
                font="Friz Quadrata TT", color={r=1,g=1,b=1,a=1},
                offsetX=0, offsetY=0,
            },
            restoration = {
                show=true, format="compact", size=12,
                font="Friz Quadrata TT", color={r=1,g=1,b=1,a=1},
                offsetX=0, offsetY=0,
            },
        },
        sparkEnabled    = true,
        flashEnabled    = true,
        bevelEnabled    = true,   -- thin bright highlight on top edge of bar
        -- Enhancement stack display
        enhSeparators   = false,          -- show divider lines between each stack
        enhSplitMode    = false,          -- 5+5 mode: refill bar with new colour after 5
        enhSplitColor   = { r=0.6, g=0.2, b=1, a=1 },  -- default purple for second phase
        -- Elemental Maelstrom markers (static lines at fixed values)
        elemMarkers     = {
            { enabled=false, value=25,  width=2, color={r=1,g=1,b=1,a=0.8} },
            { enabled=false, value=50,  width=2, color={r=1,g=1,b=1,a=0.8} },
            { enabled=false, value=75,  width=2, color={r=1,g=1,b=1,a=0.8} },
            { enabled=false, value=100, width=2, color={r=1,g=1,b=1,a=0.8} },
        },
        -- 3 thresholds for Enhancement (stack-count based)
        enhThresholds   = {
            DefaultThreshold(3,  1, 1, 0),    -- yellow at 3
            DefaultThreshold(7,  1, 0.5, 0),  -- orange at 7
            DefaultThreshold(10, 0, 1, 0),    -- green at 10
        },
    }
}

-- ── Spec detection ───────────────────────────────────────────────────
local currentSpec  = nil  -- "enhancement" | "elemental" | nil
local lastEnhStacks = -1  -- for sound edge-detection

local function RefreshSpec()
    local idx = GetSpecialization()
    if not idx then currentSpec = nil; return end
    local specID = GetSpecializationInfo(idx)
    if specID == ELEMENTAL_SPEC then
        currentSpec = "elemental"
    elseif specID == ENHANCEMENT_SPEC then
        currentSpec = "enhancement"
    elseif specID == RESTORATION_SPEC then
        currentSpec = "restoration"
    else
        currentSpec = nil
    end
    lastEnhStacks = -1  -- reset on spec change
    animLastColorTarget = -1
    animSettled = false
end

-- ── Display frame ────────────────────────────────────────────────────
local barFrame = CreateFrame("Frame", "MRB_BarFrame", UIParent)
barFrame:SetSize(200, 24)
barFrame:SetPoint("CENTER", UIParent, "CENTER", 0, 200)
barFrame:SetMovable(true)
barFrame:EnableMouse(true)
barFrame:RegisterForDrag("LeftButton")
barFrame:SetScript("OnDragStart", function(self)
    if not MRB_DB or not MRB_DB.profile.locked then self:StartMoving() end
end)
barFrame:SetScript("OnDragStop", function(self)
    self:StopMovingOrSizing()
    if MRB_DB then
        local _, _, _, x, y = self:GetPoint()
        MRB_DB.profile.posX = x
        MRB_DB.profile.posY = y
    end
end)
barFrame:SetClampedToScreen(true)
barFrame:Hide()

local bg = barFrame:CreateTexture(nil, "BACKGROUND")
bg:SetAllPoints(barFrame)
bg:SetColorTexture(0, 0, 0, 0.6)

-- ── Round corner mask ─────────────────────────────────────────────────
-- CornerMask.tga is a 64x64 white rounded rect on black — AddMaskTexture
-- clips each texture to the opaque region of the mask (white = show).
local MASK_TEX = "Interface\\AddOns\\Shaman_Resource_Bar_v1\\Textures\\CornerMask"
local roundMask = barFrame:CreateMaskTexture()
roundMask:SetTexture(MASK_TEX)
roundMask:SetAllPoints(barFrame)
bg:AddMaskTexture(roundMask)

-- Border
local borderFrame = CreateFrame("Frame", nil, barFrame)
borderFrame:SetAllPoints(barFrame)
borderFrame:SetFrameLevel(barFrame:GetFrameLevel() + 3)
local borderEdges = {
    top    = borderFrame:CreateTexture(nil, "OVERLAY"),
    bottom = borderFrame:CreateTexture(nil, "OVERLAY"),
    left   = borderFrame:CreateTexture(nil, "OVERLAY"),
    right  = borderFrame:CreateTexture(nil, "OVERLAY"),
}
for _, t in pairs(borderEdges) do t:SetColorTexture(0, 0, 0, 1) end

local function ApplyBorder()
    if not MRB_DB then return end
    local db = MRB_DB.profile
    if not db.border.show then
        for _, t in pairs(borderEdges) do t:Hide() end
        return
    end
    local bc, th = db.border.color, db.border.thickness
    for _, t in pairs(borderEdges) do t:SetColorTexture(bc.r, bc.g, bc.b, bc.a); t:Show() end
    borderEdges.top:SetPoint("TOPLEFT", borderFrame, "TOPLEFT")
    borderEdges.top:SetPoint("TOPRIGHT", borderFrame, "TOPRIGHT")
    borderEdges.top:SetHeight(th)
    borderEdges.bottom:SetPoint("BOTTOMLEFT", borderFrame, "BOTTOMLEFT")
    borderEdges.bottom:SetPoint("BOTTOMRIGHT", borderFrame, "BOTTOMRIGHT")
    borderEdges.bottom:SetHeight(th)
    borderEdges.left:SetPoint("TOPLEFT", borderFrame, "TOPLEFT")
    borderEdges.left:SetPoint("BOTTOMLEFT", borderFrame, "BOTTOMLEFT")
    borderEdges.left:SetWidth(th)
    borderEdges.right:SetPoint("TOPRIGHT", borderFrame, "TOPRIGHT")
    borderEdges.right:SetPoint("BOTTOMRIGHT", borderFrame, "BOTTOMRIGHT")
    borderEdges.right:SetWidth(th)
end

local bar = CreateFrame("StatusBar", "MRB_Bar", barFrame)
bar:SetAllPoints(barFrame)
bar:SetStatusBarTexture("Interface\\TargetingFrame\\UI-StatusBar")
bar:SetMinMaxValues(0, 10)
bar:SetValue(0)

-- Phase 2 overlay bar for 5+5 mode — sits above bar, same texture, fills on top
local bar2 = CreateFrame("StatusBar", "MRB_Bar2", barFrame)
bar2:SetAllPoints(barFrame)
bar2:SetStatusBarTexture("Interface\\TargetingFrame\\UI-StatusBar")
bar2:SetFrameLevel(bar:GetFrameLevel() + 1)
bar2:SetMinMaxValues(0, 5)
bar2:SetValue(0)
bar2:Hide()
-- Opaque background on bar2 blocks bar's fill colour from bleeding through
-- the unfilled portion of bar2 in split mode.
local bar2Bg = bar2:CreateTexture(nil, "BACKGROUND")
bar2Bg:SetAllPoints(bar2)
bar2Bg:SetColorTexture(0, 0, 0, 0)  -- invisible until split mode active

-- Label sits on its own frame above borderFrame (level+3) and separators
local labelFrame = CreateFrame("Frame", nil, barFrame)
labelFrame:SetAllPoints(barFrame)
labelFrame:SetFrameLevel(barFrame:GetFrameLevel() + 5)
local label = labelFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
label:SetPoint("CENTER", labelFrame, "CENTER", 0, 0)
label:SetText("")

-- Spark — bright vertical flicker at the bar's leading edge (Enhancement only)
local spark = barFrame:CreateTexture(nil, "OVERLAY")
spark:SetTexture("Interface\\CastingBar\\UI-CastingBar-Spark")
spark:SetBlendMode("ADD")
spark:SetSize(8, 32)
spark:SetAlpha(0.9)
spark:Hide()

-- Flash overlay — briefly pulses when crossing a threshold
local flashTex = barFrame:CreateTexture(nil, "OVERLAY")
flashTex:SetAllPoints(barFrame)
flashTex:SetBlendMode("ADD")
flashTex:SetColorTexture(1, 1, 1, 1)
flashTex:SetAlpha(0)
flashTex:Hide()

-- ── Bevel highlight ───────────────────────────────────────────────────
-- A thin bright line along the top edge gives the bar a subtle raised look.
-- Lives on glossFrame (level+4) so it's above the fill, below the label.
local glossFrame = CreateFrame("Frame", nil, barFrame)
glossFrame:SetAllPoints(barFrame)
glossFrame:SetFrameLevel(barFrame:GetFrameLevel() + 4)
local bevelTex = glossFrame:CreateTexture(nil, "ARTWORK")
bevelTex:SetColorTexture(1, 1, 1, 0.25)
bevelTex:SetHeight(1)
bevelTex:SetPoint("TOPLEFT",  barFrame, "TOPLEFT",  0, -1)
bevelTex:SetPoint("TOPRIGHT", barFrame, "TOPRIGHT", 0, -1)
bevelTex:Hide()

-- Stack separators — 9 thin vertical lines for Enhancement
local separators = {}
for i = 1, 9 do
    local s = borderFrame:CreateTexture(nil, "OVERLAY")
    s:SetColorTexture(0, 0, 0, 0.8)
    s:Hide()
    separators[i] = s
end

-- Elemental markers — separate pool so they can have independent style
local elemMarkerTextures = {}
for i = 1, 4 do
    local t = borderFrame:CreateTexture(nil, "OVERLAY")
    t:SetColorTexture(1, 1, 1, 0.8)
    t:Hide()
    elemMarkerTextures[i] = t
end

local function ApplySeparators()
    if not MRB_DB then return end
    local db = MRB_DB.profile
    local w  = barFrame:GetWidth()

    -- Hide all first
    for i = 1, 9 do separators[i]:Hide() end
    if w == 0 then return end

    if currentSpec == "enhancement" and db.enhSeparators then
        local divisions = db.enhSplitMode and 5 or 10
        local count     = divisions - 1
        for i = 1, count do
            local x = (i / divisions) * w
            separators[i]:ClearAllPoints()
            separators[i]:SetPoint("TOPLEFT",    barFrame, "TOPLEFT",    x - 0.5, 0)
            separators[i]:SetPoint("BOTTOMLEFT", barFrame, "BOTTOMLEFT", x - 0.5, 0)
            separators[i]:SetWidth(1)
            separators[i]:Show()
        end

    elseif currentSpec == "elemental" then
        -- Position markers using their own texture pool
        local maxPower = UnitPowerMax("player", MAELSTROM_POWER)
        if not maxPower or maxPower <= 0 then
            for i = 1, 4 do elemMarkerTextures[i]:Hide() end
            return
        end
        for i = 1, 4 do
            local m = db.elemMarkers[i]
            local t = elemMarkerTextures[i]
            if m.enabled and m.value > 0 and m.value < maxPower then
                local sc = m.color or {r=1,g=1,b=1,a=0.8}
                local sw = math.max(1, m.width or 2)
                local x  = (m.value / maxPower) * w
                t:SetColorTexture(sc.r, sc.g, sc.b, sc.a)
                t:ClearAllPoints()
                t:SetPoint("TOPLEFT",    barFrame, "TOPLEFT",    x - sw * 0.5, 0)
                t:SetPoint("BOTTOMLEFT", barFrame, "BOTTOMLEFT", x - sw * 0.5, 0)
                t:SetWidth(sw)
                t:Show()
            else
                t:Hide()
            end
        end
    else
        for i = 1, 4 do elemMarkerTextures[i]:Hide() end
    end
end

-- ── ApplyPolish ───────────────────────────────────────────────────────
-- Called from ApplySettings. Re-binds the round mask (lost on SetStatusBarTexture),
-- positions shadow edges, and syncs bevel/shadow visibility.
local function ApplyPolish()
    if not MRB_DB then return end
    local db = MRB_DB.profile

    -- Re-bind mask to fill textures (lost on SetStatusBarTexture; remove first to avoid stacking)
    bg:RemoveMaskTexture(roundMask)
    bg:AddMaskTexture(roundMask)
    local t1 = bar:GetStatusBarTexture()
    if t1 then t1:RemoveMaskTexture(roundMask) t1:AddMaskTexture(roundMask) end
    local t2 = bar2:GetStatusBarTexture()
    if t2 then t2:RemoveMaskTexture(roundMask) t2:AddMaskTexture(roundMask) end
    flashTex:RemoveMaskTexture(roundMask)
    flashTex:AddMaskTexture(roundMask)

    -- Bevel
    bevelTex:SetShown(db.bevelEnabled)

    -- Shadow — reposition in case bar was resized
end

-- ── Label font ────────────────────────────────────────────────────────
-- Returns the label settings table for the currently active spec.
local function GetLabelCfg()
    if not MRB_DB then return nil end
    local specs = MRB_DB.profile.labelSpecs
    local key = currentSpec or "enhancement"
    return specs and specs[key]
end

local function ApplyLabelFont()
    if not MRB_DB then return end
    local cfg = GetLabelCfg()
    if not cfg then return end
    local fontPath = "Fonts\\FRIZQT__.TTF"
    if LSM then
        fontPath = LSM:Fetch(LSM.MediaType.FONT, cfg.font) or fontPath
    end
    local lc = cfg.color
    label:SetFont(fontPath, cfg.size, "OUTLINE")
    label:SetTextColor(lc.r, lc.g, lc.b, lc.a)
    label:ClearAllPoints()
    label:SetPoint("CENTER", labelFrame, "CENTER", cfg.offsetX or 0, cfg.offsetY or 0)
end

-- ─────────────────────────────────────────────────────────────────────
local function ApplySettings()
    if not MRB_DB then return end
    local db = MRB_DB.profile
    barFrame:SetSize(db.width, db.height)
    barFrame:SetMovable(not db.locked)
    barFrame:EnableMouse(not db.locked)
    barFrame:ClearAllPoints()
    if db.posX and db.posY then
        barFrame:SetPoint("CENTER", UIParent, "CENTER", db.posX, db.posY)
    else
        barFrame:SetPoint("CENTER", UIParent, "CENTER", 0, 200)
    end
    local bgc = db.bgColor
    bg:SetColorTexture(bgc.r, bgc.g, bgc.b, bgc.a)
    local tex = GetBarTexture(db.barTexture or "Blizzard")
    bar:SetStatusBarTexture(tex)
    bar2:SetStatusBarTexture(tex)
    ApplyBorder()
    ApplyLabelFont()
    ApplySeparators()
    ApplyPolish()
    do local lc=GetLabelCfg(); label:SetShown(lc and lc.show ~= false) end
end

-- ── Animation state (Enhancement only — Elemental values are tainted) ─
local animCurrent  = 0      -- current rendered bar value
local animTarget   = 0      -- target stack count (set by UpdateDisplay)
local animFrom     = 0      -- value when current transition started
local animElapsed  = 0      -- time into the current transition
local ANIM_DURATION = 0.2   -- seconds for a full glide

-- Cached bar color — only recomputed when animTarget changes
local animLastColorTarget = -1
local animCachedR, animCachedG, animCachedB, animCachedA = 1, 1, 1, 1
local animSettled = false   -- true once lerp has fully completed and final state written

-- Flash state
local flashAlpha     = 0
local FLASH_DURATION = 0.35

-- ── Threshold helpers ─────────────────────────────────────────────────
local function GetEnhBarColor(stacks, db)
    local thresholds = db.enhThresholds
    for i = 3, 1, -1 do
        local t = thresholds[i]
        if t.enabled and stacks >= t.stacks then
            return t.color.r, t.color.g, t.color.b, t.color.a
        end
    end
    local c = db.enhColor
    return c.r, c.g, c.b, c.a
end

-- Fire sounds and flash for thresholds crossed upward.
local function CheckEnhEffects(prev, cur, db)
    if prev < 0 then return end
    local thresholds = db.enhThresholds
    for i = 1, 3 do
        local t = thresholds[i]
        if t.enabled and prev < t.stacks and cur >= t.stacks then
            -- Sound
            if t.sound ~= "-- None --" then PlayThresholdSound(t.sound) end
            -- Flash (colour-matched to the threshold)
            if db.flashEnabled then
                local c = t.color
                flashTex:SetColorTexture(c.r, c.g, c.b, 1)
                flashAlpha = 0.55
                flashTex:SetAlpha(flashAlpha)
                flashTex:Show()
            end
        end
    end
end

-- ── Update display ────────────────────────────────────────────────────
local function UpdateDisplay()
    if not MRB_DB then return end
    local db = MRB_DB.profile

    -- Visibility checks — hide if any active condition says so
    if db.hideOutOfCombat and not (UnitAffectingCombat("player") or InCombatLockdown()) then
        barFrame:Hide(); return
    end
    if db.hideMounted and IsMounted() then
        barFrame:Hide(); return
    end
    if db.hideVehicle and (UnitInVehicle("player") or UnitHasVehicleUI("player")) then
        barFrame:Hide(); return
    end
    if db.hidePetBattle and C_PetBattles.IsInBattle() then
        barFrame:Hide(); return
    end

    if currentSpec == "elemental" then
        local rawCur = UnitPower("player", MAELSTROM_POWER)
        local rawMax = UnitPowerMax("player", MAELSTROM_POWER)
        local c = db.elemColor
        bar:SetMinMaxValues(0, rawMax)
        bar:SetValue(rawCur)
        bar:SetStatusBarColor(c.r, c.g, c.b, c.a)
        ApplySeparators()
        local _lcfg = GetLabelCfg()
        if _lcfg and _lcfg.show ~= false then
            if _lcfg.format == "value" then
                label:SetText(AbbreviateNumbers(rawCur))
            else
                label:SetText(AbbreviateNumbers(rawCur) .. "/" .. AbbreviateNumbers(rawMax))
            end
        else
            label:SetText("")
        end
        barFrame:Show()

    elseif currentSpec == "enhancement" then
        local aura   = C_UnitAuras.GetPlayerAuraBySpellID(MSW_SPELL_ID)
        local stacks = (aura and aura.applications) or 0
        -- Skip full update if stacks haven't changed (animFrame handles the rest)
        if stacks == lastEnhStacks and barFrame:IsShown() then return end
        -- Drive animation target — animFrame owns ALL bar visual writes
        if stacks ~= animTarget then
            animFrom    = animCurrent
            animElapsed = 0
            animTarget  = stacks
            animSettled = false
        end
        local _lcfg = GetLabelCfg()
        if _lcfg and _lcfg.show ~= false then
            if _lcfg.format == "value" then
                label:SetText(tostring(stacks))
            else
                label:SetText(tostring(stacks) .. "/10")
            end
        else
            label:SetText("")
        end
        CheckEnhEffects(lastEnhStacks, stacks, db)
        lastEnhStacks = stacks
        barFrame:Show()

    elseif currentSpec == "restoration" then
        local rawCur = UnitPower("player", MANA_POWER)
        local rawMax = UnitPowerMax("player", MANA_POWER)
        local c = db.restColor
        bar:SetMinMaxValues(0, rawMax)
        bar:SetValue(rawCur)
        bar:SetStatusBarColor(c.r, c.g, c.b, c.a)
        local _lcfg = GetLabelCfg()
        if _lcfg and _lcfg.show ~= false then
            if _lcfg.format == "value" then
                label:SetText(AbbreviateNumbers(rawCur))
            else
                label:SetText(AbbreviateNumbers(rawCur) .. "/" .. AbbreviateNumbers(rawMax))
            end
        else
            label:SetText("")
        end
        barFrame:Show()

    else
        for i = 1, 9 do separators[i]:Hide() end
        barFrame:Hide()
    end
end

-- ── Options panel ─────────────────────────────────────────────────────
local function BuildOptions()
    local AceConfig       = LibStub("AceConfig-3.0")
    local AceConfigDialog = LibStub("AceConfigDialog-3.0")

    -- ── Tab: Bar ─────────────────────────────────────────────────────
    local tabBar = {
        type = "group", order = 1,
        name = "|cffffd100Bar|r",
        args = {
            textureGroup = {
                type = "group", inline = true, order = 1,
                name = "|cff00ccffTexture|r",
                args = {
                    desc = {
                        type = "description", order = 0,
                        name = "Choose the fill texture for the status bar.",
                        fontSize = "small",
                    },
                    barTexture = {
                        type = "select", name = "Bar texture", order = 1,
                        width = "full",
                        dialogControl = "LSM30_Statusbar",
                        values = function()
                            if LSM then return LSM:HashTable(LSM.MediaType.STATUSBAR) end
                            return { ["Blizzard"] = "Blizzard" }
                        end,
                        get = function() return MRB_DB.profile.barTexture or "Blizzard" end,
                        set = function(_, v)
                            MRB_DB.profile.barTexture = v
                            local tex = GetBarTexture(v)
                            bar:SetStatusBarTexture(tex)
                            bar2:SetStatusBarTexture(tex)
                            ApplyPolish()
                        end,
                    },
                },
            },
            sizeGroup = {
                type = "group", inline = true, order = 2,
                name = "|cff00ccffSize|r",
                args = {
                    width = {
                        type = "range", name = "Width", order = 1,
                        width = "full",
                        min = 50, max = 600, step = 1,
                        get = function() return MRB_DB.profile.width end,
                        set = function(_, v) MRB_DB.profile.width=v; ApplySettings(); UpdateDisplay() end,
                    },
                    height = {
                        type = "range", name = "Height", order = 2,
                        width = "full",
                        min = 4, max = 100, step = 1,
                        get = function() return MRB_DB.profile.height end,
                        set = function(_, v) MRB_DB.profile.height=v; ApplySettings(); UpdateDisplay() end,
                    },
                },
            },
            posGroup = {
                type = "group", inline = true, order = 3,
                name = "|cff00ccffPosition|r",
                args = {
                    locked = {
                        type = "toggle", name = "Lock bar", order = 1,
                        desc = "Prevent the bar from being dragged.",
                        get  = function() return MRB_DB.profile.locked end,
                        set  = function(_, v) MRB_DB.profile.locked=v; ApplySettings() end,
                    },
                    resetPos = {
                        type = "execute", name = "Reset Position", order = 2,
                        desc = "Move the bar back to the default centre position.",
                        func = function()
                            MRB_DB.profile.posX=nil; MRB_DB.profile.posY=nil; ApplySettings()
                        end,
                    },
                },
            },
            enhStyleGroup = {
                type = "group", inline = true, order = 4,
                name = "|cffff9900Enhancement Style|r",
                args = {
                    desc = {
                        type = "description", order = 0,
                        name = "Spark, flash, separators and split mode are Enhancement only.",
                        fontSize = "small",
                    },
                    sparkEnabled = {
                        type = "toggle", name = "Spark at bar edge", order = 1,
                        desc = "Show a bright flicker at the leading edge of the bar as it fills.",
                        disabled = function() return currentSpec ~= "enhancement" end,
                        get  = function() return MRB_DB.profile.sparkEnabled end,
                        set  = function(_, v) MRB_DB.profile.sparkEnabled = v end,
                    },
                    flashEnabled = {
                        type = "toggle", name = "Flash on threshold", order = 2,
                        desc = "Briefly pulse the bar colour when crossing a threshold.",
                        disabled = function() return currentSpec ~= "enhancement" end,
                        get  = function() return MRB_DB.profile.flashEnabled end,
                        set  = function(_, v) MRB_DB.profile.flashEnabled = v end,
                    },
                    enhSeparators = {
                        type = "toggle", name = "Show stack separators", order = 3,
                        desc = "Draw a divider line between each stack segment.",
                        disabled = function() return currentSpec ~= "enhancement" end,
                        get  = function() return MRB_DB.profile.enhSeparators end,
                        set  = function(_, v)
                            MRB_DB.profile.enhSeparators = v
                            ApplySeparators()
                        end,
                    },
                    enhSplitMode = {
                        type = "toggle", name = "5+5 split mode", order = 4,
                        desc = "Bar fills to 5 stacks, then resets and fills again in a second colour. Separators will show 4 lines instead of 9.",
                        disabled = function() return currentSpec ~= "enhancement" end,
                        get  = function() return MRB_DB.profile.enhSplitMode end,
                        set  = function(_, v)
                            MRB_DB.profile.enhSplitMode = v
                            ApplySeparators()
                            UpdateDisplay()
                        end,
                    },
                    enhSplitColor = {
                        type = "color", name = "Phase 2 colour", order = 5,
                        desc = "Bar colour for stacks 6–10 in 5+5 mode.",
                        hasAlpha = true,
                        disabled = function() return currentSpec ~= "enhancement" end,
                        get  = function()
                            local c = MRB_DB.profile.enhSplitColor
                            return c.r, c.g, c.b, c.a
                        end,
                        set  = function(_, r, g, b, a)
                            MRB_DB.profile.enhSplitColor = {r=r,g=g,b=b,a=a}
                            UpdateDisplay()
                        end,
                    },
                },
            },
            appearanceGroup = {
                type = "group", inline = true, order = 5,
                name = "|cff00ccffAppearance|r",
                args = {
                    bevelEnabled = {
                        type = "toggle", name = "Bevel highlight", order = 1,
                        desc = "Draw a thin bright line along the top edge of the bar.",
                        get  = function() return MRB_DB.profile.bevelEnabled end,
                        set  = function(_, v)
                            MRB_DB.profile.bevelEnabled = v
                            bevelTex:SetShown(v)
                        end,
                    },
                },
            },
        },
    }

    -- ── Tab: Visibility ───────────────────────────────────────────────
    local tabVisibility = {
        type = "group", order = 2,
        name = "|cffffd100Visibility|r",
        args = {
            combatGroup = {
                type = "group", inline = true, order = 1,
                name = "|cff00ccffCombat|r",
                args = {
                    hideOutOfCombat = {
                        type = "toggle", name = "Hide out of combat", order = 1,
                        width = "full",
                        desc = "Hide the bar when you are not in combat.",
                        get  = function() return MRB_DB.profile.hideOutOfCombat end,
                        set  = function(_, v) MRB_DB.profile.hideOutOfCombat=v; UpdateDisplay() end,
                    },
                },
            },
            worldGroup = {
                type = "group", inline = true, order = 2,
                name = "|cff00ccffWorld|r",
                args = {
                    hideMounted = {
                        type = "toggle", name = "Hide when mounted", order = 1,
                        width = "full",
                        desc = "Hide the bar while riding a mount.",
                        get  = function() return MRB_DB.profile.hideMounted end,
                        set  = function(_, v) MRB_DB.profile.hideMounted=v; UpdateDisplay() end,
                    },
                    hideVehicle = {
                        type = "toggle", name = "Hide in vehicle", order = 2,
                        width = "full",
                        desc = "Hide the bar when controlling a vehicle.",
                        get  = function() return MRB_DB.profile.hideVehicle end,
                        set  = function(_, v) MRB_DB.profile.hideVehicle=v; UpdateDisplay() end,
                    },
                    hidePetBattle = {
                        type = "toggle", name = "Hide in pet battles", order = 3,
                        width = "full",
                        desc = "Hide the bar during pet battles.",
                        get  = function() return MRB_DB.profile.hidePetBattle end,
                        set  = function(_, v) MRB_DB.profile.hidePetBattle=v; UpdateDisplay() end,
                    },
                },
            },
        },
    }

    -- ── Tab: Label ────────────────────────────────────────────────────
    -- Helper: build the args table for one spec's label settings
    local function MakeLabelSpecArgs(specKey)
        local s = MRB_DB.profile.labelSpecs[specKey]
        return {
            showLabel = {
                type = "toggle", name = "Show label", order = 1, width = "full",
                get  = function() return MRB_DB.profile.labelSpecs[specKey].show ~= false end,
                set  = function(_, v) MRB_DB.profile.labelSpecs[specKey].show=v; UpdateDisplay() end,
            },
            labelFormat = {
                type = "select", name = "Format", order = 2, width = "double",
                values = { compact="X/Max  (e.g. 7/10)", value="X  (value only)" },
                get  = function() return MRB_DB.profile.labelSpecs[specKey].format or "compact" end,
                set  = function(_, v) MRB_DB.profile.labelSpecs[specKey].format=v; UpdateDisplay() end,
            },
            fontGroup = {
                type = "group", inline = true, order = 3, name = "|cff00ccffFont|r",
                args = {
                    labelFont = {
                        type = "select", name = "Font", order = 1, width = "double",
                        dialogControl = "LSM30_Font",
                        values = function()
                            if LSM then return LSM:HashTable(LSM.MediaType.FONT) end
                            return { ["Friz Quadrata TT"] = "Friz Quadrata TT" }
                        end,
                        get  = function() return MRB_DB.profile.labelSpecs[specKey].font end,
                        set  = function(_, v) MRB_DB.profile.labelSpecs[specKey].font=v; ApplyLabelFont() end,
                    },
                    labelSize = {
                        type = "range", name = "Size", order = 2,
                        min = 6, max = 36, step = 1,
                        get  = function() return MRB_DB.profile.labelSpecs[specKey].size end,
                        set  = function(_, v) MRB_DB.profile.labelSpecs[specKey].size=v; ApplyLabelFont() end,
                    },
                    labelColor = {
                        type = "color", name = "Colour", order = 3, hasAlpha = true,
                        get  = function() local c=MRB_DB.profile.labelSpecs[specKey].color; return c.r,c.g,c.b,c.a end,
                        set  = function(_,r,g,b,a) MRB_DB.profile.labelSpecs[specKey].color={r=r,g=g,b=b,a=a}; ApplyLabelFont() end,
                    },
                },
            },
            offsetGroup = {
                type = "group", inline = true, order = 4, name = "|cff00ccffPosition|r",
                args = {
                    offsetX = {
                        type = "range", name = "Offset X", order = 1,
                        min = -200, max = 200, step = 1,
                        get  = function() return MRB_DB.profile.labelSpecs[specKey].offsetX or 0 end,
                        set  = function(_, v) MRB_DB.profile.labelSpecs[specKey].offsetX=v; ApplyLabelFont() end,
                    },
                    offsetY = {
                        type = "range", name = "Offset Y", order = 2,
                        min = -200, max = 200, step = 1,
                        get  = function() return MRB_DB.profile.labelSpecs[specKey].offsetY or 0 end,
                        set  = function(_, v) MRB_DB.profile.labelSpecs[specKey].offsetY=v; ApplyLabelFont() end,
                    },
                    resetOffset = {
                        type = "execute", name = "Reset Position", order = 3,
                        func = function()
                            MRB_DB.profile.labelSpecs[specKey].offsetX = 0
                            MRB_DB.profile.labelSpecs[specKey].offsetY = 0
                            ApplyLabelFont()
                        end,
                    },
                },
            },
        }
    end

    local tabLabel = {
        type = "group", order = 3,
        name = "|cffffd100Label|r",
        childGroups = "tab",
        args = {
            allSpecs = {
                type = "group", order = 1,
                name = "|cffffffffAll Specs|r",
                args = {
                    applyNote = {
                        type = "description", order = 0, fontSize = "medium",
                        name = "Changes here are applied to all three specs at once.",
                    },
                    showLabel = {
                        type = "toggle", name = "Show label", order = 1, width = "full",
                        get  = function() return MRB_DB.profile.labelSpecs.enhancement.show ~= false end,
                        set  = function(_, v)
                            for _, s in ipairs({"enhancement","elemental","restoration"}) do
                                MRB_DB.profile.labelSpecs[s].show = v
                            end
                            UpdateDisplay()
                        end,
                    },
                    labelFormat = {
                        type = "select", name = "Format", order = 2, width = "double",
                        values = { compact="X/Max  (e.g. 7/10)", value="X  (value only)" },
                        get  = function() return MRB_DB.profile.labelSpecs.enhancement.format or "compact" end,
                        set  = function(_, v)
                            for _, s in ipairs({"enhancement","elemental","restoration"}) do
                                MRB_DB.profile.labelSpecs[s].format = v
                            end
                            UpdateDisplay()
                        end,
                    },
                    fontGroup = {
                        type = "group", inline = true, order = 3, name = "|cff00ccffFont|r",
                        args = {
                            labelFont = {
                                type = "select", name = "Font", order = 1, width = "double",
                                dialogControl = "LSM30_Font",
                                values = function()
                                    if LSM then return LSM:HashTable(LSM.MediaType.FONT) end
                                    return { ["Friz Quadrata TT"] = "Friz Quadrata TT" }
                                end,
                                get  = function() return MRB_DB.profile.labelSpecs.enhancement.font end,
                                set  = function(_, v)
                                    for _, s in ipairs({"enhancement","elemental","restoration"}) do
                                        MRB_DB.profile.labelSpecs[s].font = v
                                    end
                                    ApplyLabelFont()
                                end,
                            },
                            labelSize = {
                                type = "range", name = "Size", order = 2,
                                min = 6, max = 36, step = 1,
                                get  = function() return MRB_DB.profile.labelSpecs.enhancement.size end,
                                set  = function(_, v)
                                    for _, s in ipairs({"enhancement","elemental","restoration"}) do
                                        MRB_DB.profile.labelSpecs[s].size = v
                                    end
                                    ApplyLabelFont()
                                end,
                            },
                            labelColor = {
                                type = "color", name = "Colour", order = 3, hasAlpha = true,
                                get  = function()
                                    local c = MRB_DB.profile.labelSpecs.enhancement.color
                                    return c.r, c.g, c.b, c.a
                                end,
                                set  = function(_, r, g, b, a)
                                    for _, s in ipairs({"enhancement","elemental","restoration"}) do
                                        MRB_DB.profile.labelSpecs[s].color = {r=r,g=g,b=b,a=a}
                                    end
                                    ApplyLabelFont()
                                end,
                            },
                        },
                    },
                    offsetGroup = {
                        type = "group", inline = true, order = 4, name = "|cff00ccffPosition|r",
                        args = {
                            offsetX = {
                                type = "range", name = "Offset X", order = 1,
                                min = -200, max = 200, step = 1,
                                get  = function() return MRB_DB.profile.labelSpecs.enhancement.offsetX or 0 end,
                                set  = function(_, v)
                                    for _, s in ipairs({"enhancement","elemental","restoration"}) do
                                        MRB_DB.profile.labelSpecs[s].offsetX = v
                                    end
                                    ApplyLabelFont()
                                end,
                            },
                            offsetY = {
                                type = "range", name = "Offset Y", order = 2,
                                min = -200, max = 200, step = 1,
                                get  = function() return MRB_DB.profile.labelSpecs.enhancement.offsetY or 0 end,
                                set  = function(_, v)
                                    for _, s in ipairs({"enhancement","elemental","restoration"}) do
                                        MRB_DB.profile.labelSpecs[s].offsetY = v
                                    end
                                    ApplyLabelFont()
                                end,
                            },
                            resetOffset = {
                                type = "execute", name = "Reset Position", order = 3,
                                func = function()
                                    for _, s in ipairs({"enhancement","elemental","restoration"}) do
                                        MRB_DB.profile.labelSpecs[s].offsetX = 0
                                        MRB_DB.profile.labelSpecs[s].offsetY = 0
                                    end
                                    ApplyLabelFont()
                                end,
                            },
                        },
                    },
                },
            },
            enhancement = {
                type = "group", order = 2,
                name = "|cffff9900Enhancement|r",
                args = MakeLabelSpecArgs("enhancement"),
            },
            elemental = {
                type = "group", order = 3,
                name = "|cff0099ffElemental|r",
                args = MakeLabelSpecArgs("elemental"),
            },
            restoration = {
                type = "group", order = 4,
                name = "|cff00cc44Restoration|r",
                args = MakeLabelSpecArgs("restoration"),
            },
        },
    }

    -- ── Tab: Colours ──────────────────────────────────────────────────
    local tabColours = {
        type = "group", order = 4,
        name = "|cffffd100Colours|r",
        args = {
            specGroup = {
                type = "group", inline = true, order = 1,
                name = "|cff00ccffSpec Colours|r",
                args = {
                    desc = {
                        type = "description", order = 0,
                        name = "Base bar colour per spec. Thresholds can override the Enhancement colour when active.",
                        fontSize = "small",
                    },
                    enhColor = {
                        type = "color", name = "Enhancement", order = 1,
                        hasAlpha = true,
                        get = function() local c=MRB_DB.profile.enhColor; return c.r,c.g,c.b,c.a end,
                        set = function(_,r,g,b,a) MRB_DB.profile.enhColor={r=r,g=g,b=b,a=a}; UpdateDisplay() end,
                    },
                    elemColor = {
                        type = "color", name = "Elemental", order = 2,
                        hasAlpha = true,
                        get = function() local c=MRB_DB.profile.elemColor; return c.r,c.g,c.b,c.a end,
                        set = function(_,r,g,b,a) MRB_DB.profile.elemColor={r=r,g=g,b=b,a=a}; UpdateDisplay() end,
                    },
                    restColor = {
                        type = "color", name = "Restoration", order = 3,
                        hasAlpha = true,
                        get = function() local c=MRB_DB.profile.restColor; return c.r,c.g,c.b,c.a end,
                        set = function(_,r,g,b,a) MRB_DB.profile.restColor={r=r,g=g,b=b,a=a}; UpdateDisplay() end,
                    },
                },
            },
            bgGroup = {
                type = "group", inline = true, order = 2,
                name = "|cff00ccffBackground|r",
                args = {
                    bgColor = {
                        type = "color", name = "Background colour", order = 1,
                        hasAlpha = true,
                        get = function() local c=MRB_DB.profile.bgColor; return c.r,c.g,c.b,c.a end,
                        set = function(_,r,g,b,a) MRB_DB.profile.bgColor={r=r,g=g,b=b,a=a}; ApplySettings() end,
                    },
                },
            },
        },
    }

    -- ── Tab: Border ───────────────────────────────────────────────────
    local tabBorder = {
        type = "group", order = 5,
        name = "|cffffd100Border|r",
        args = {
            borderGroup = {
                type = "group", inline = true, order = 1,
                name = "|cff00ccffBorder Settings|r",
                args = {
                    borderShow = {
                        type = "toggle", name = "Show border", order = 1,
                        width = "full",
                        get  = function() return MRB_DB.profile.border.show end,
                        set  = function(_, v) MRB_DB.profile.border.show=v; ApplyBorder() end,
                    },
                    borderThickness = {
                        type = "range", name = "Thickness", order = 2,
                        width = "full",
                        min = 1, max = 10, step = 1,
                        get  = function() return MRB_DB.profile.border.thickness end,
                        set  = function(_, v) MRB_DB.profile.border.thickness=v; ApplyBorder() end,
                    },
                    borderColor = {
                        type = "color", name = "Border colour", order = 3,
                        hasAlpha = true,
                        get  = function() local c=MRB_DB.profile.border.color; return c.r,c.g,c.b,c.a end,
                        set  = function(_,r,g,b,a) MRB_DB.profile.border.color={r=r,g=g,b=b,a=a}; ApplyBorder() end,
                    },
                },
            },
        },
    }

    -- ── Tab: Thresholds ───────────────────────────────────────────────
    local tabThresholds = {
        type = "group", order = 6,
        name = "|cffffd100Thresholds|r",
        childGroups = "tab",
        args = {
            enhTab = {
                type = "group", order = 1,
                name = "|cffff9900Enhancement|r",
                args = {
                    note = {
                        type = "group", inline = true, order = 1,
                        name = "|cff00ccffAbout|r",
                        args = {
                            desc = { type="description", order=1, fontSize="medium",
                                name = "Thresholds are checked highest-first — the top active threshold whose stack count is met overrides the bar colour. Sounds fire when stacks cross upward through the trigger value.",
                            },
                        },
                    },
                },
            },
            elemTab = {
                type = "group", order = 2,
                name = "|cff0099ffElemental|r",
                args = {
                    note = {
                        type = "group", inline = true, order = 1,
                        name = "|cff00ccffAbout|r",
                        args = {
                            desc = { type="description", order=1, fontSize="medium",
                                name = "Place up to 4 static marker lines at fixed Maelstrom values. Markers automatically rescale when your maximum Maelstrom changes (e.g. from talents).",
                            },
                        },
                    },

                },
            },

        },
    }

    -- Enhancement threshold groups
    local threshNames = { "Threshold 1", "Threshold 2", "Threshold 3" }
    for i = 1, 3 do
        tabThresholds.args.enhTab.args["t"..i.."group"] = {
            type   = "group", inline = true, order = i + 1,
            name   = threshNames[i],
            args   = {
                enabled = {
                    type = "toggle", name = "Enable", order = 1,
                    get  = function() return MRB_DB.profile.enhThresholds[i].enabled end,
                    set  = function(_, v) MRB_DB.profile.enhThresholds[i].enabled=v; UpdateDisplay() end,
                },
                stacks = {
                    type = "range", name = "Trigger at stacks", order = 2,
                    min = 1, max = 10, step = 1,
                    get  = function() return MRB_DB.profile.enhThresholds[i].stacks end,
                    set  = function(_, v) MRB_DB.profile.enhThresholds[i].stacks=v; UpdateDisplay() end,
                },
                color = {
                    type = "color", name = "Bar colour", order = 3, hasAlpha = true,
                    get  = function() local c=MRB_DB.profile.enhThresholds[i].color; return c.r,c.g,c.b,c.a end,
                    set  = function(_,r,g,b,a) MRB_DB.profile.enhThresholds[i].color={r=r,g=g,b=b,a=a}; UpdateDisplay() end,
                },
                spacer = { type="description", name=" ", order=4, width="full" },
                sound = {
                    type   = "select", name = "Sound on trigger", order = 5,
                    width  = "double",
                    values = THRESHOLD_SOUND_LIST,
                    get    = function() return MRB_DB.profile.enhThresholds[i].sound end,
                    set    = function(_, v) MRB_DB.profile.enhThresholds[i].sound=v end,
                },
                test = {
                    type = "execute", name = "Test", order = 6,
                    desc = "Play the selected sound now.",
                    func = function() PlayThresholdSound(MRB_DB.profile.enhThresholds[i].sound) end,
                },
            },
        }
    end

    -- Elemental marker groups
    local markerNames = { "Marker 1", "Marker 2", "Marker 3", "Marker 4" }
    for i = 1, 4 do
        tabThresholds.args.elemTab.args["m"..i.."group"] = {
            type   = "group", inline = true, order = i + 1,
            name   = markerNames[i],
            args   = {
                enabled = {
                    type = "toggle", name = "Enable", order = 1,
                    get  = function() return MRB_DB.profile.elemMarkers[i].enabled end,
                    set  = function(_, v)
                        MRB_DB.profile.elemMarkers[i].enabled = v
                        ApplySeparators()
                    end,
                },
                value = {
                    type = "range", name = "At Maelstrom", order = 2,
                    min = 1, max = 250, step = 1,
                    get  = function() return MRB_DB.profile.elemMarkers[i].value end,
                    set  = function(_, v)
                        MRB_DB.profile.elemMarkers[i].value = v
                        ApplySeparators()
                    end,
                },
                color = {
                    type = "color", name = "Colour", order = 3, hasAlpha = true,
                    get  = function()
                        local c = MRB_DB.profile.elemMarkers[i].color
                        return c.r, c.g, c.b, c.a
                    end,
                    set  = function(_, r, g, b, a)
                        MRB_DB.profile.elemMarkers[i].color = {r=r,g=g,b=b,a=a}
                        ApplySeparators()
                    end,
                },
                width = {
                    type = "range", name = "Width", order = 4,
                    min = 1, max = 6, step = 1,
                    get  = function() return MRB_DB.profile.elemMarkers[i].width or 2 end,
                    set  = function(_, v)
                        MRB_DB.profile.elemMarkers[i].width = v
                        ApplySeparators()
                    end,
                },
            },
        }
    end


    -- ── Root (tabbed) ─────────────────────────────────────────────────
    local options = {
        type        = "group",
        name        = "Shaman Resource Bar",
        childGroups = "tab",
        args = {
            bar         = tabBar,
            visibility  = tabVisibility,
            label       = tabLabel,
            colours     = tabColours,
            border      = tabBorder,
            thresholds  = tabThresholds,
        },
    }

    AceConfig:RegisterOptionsTable("ShamanResourceBar", options)
    -- Keep a Blizzard panel entry so the addon appears in Interface → AddOns
    AceConfigDialog:AddToBlizOptions("ShamanResourceBar", "Shaman Resource Bar")
    -- Set a default window size comfortable for the tabbed layout
    AceConfigDialog:SetDefaultSize("ShamanResourceBar", 660, 540)
end

-- ── DB init helper ────────────────────────────────────────────────────
local function InitDB()
    MRB_DB = MRB_DB or {}
    MRB_DB.profile = MRB_DB.profile or {}
    local p = MRB_DB.profile
    local d = DB_DEFAULTS.profile

    for k, v in pairs(d) do
        if p[k] == nil then p[k] = v end
    end

    -- Nested colour tables
    for _, key in ipairs({"enhColor","elemColor","restColor","bgColor","enhSplitColor"}) do
        if type(p[key]) ~= "table" then p[key] = d[key] end
    end

    -- Per-spec label settings
    if type(p.labelSpecs) ~= "table" then
        p.labelSpecs = d.labelSpecs
    else
        for _, spec in ipairs({"enhancement","elemental","restoration"}) do
            if type(p.labelSpecs[spec]) ~= "table" then
                p.labelSpecs[spec] = d.labelSpecs[spec]
            else
                local ps, ds = p.labelSpecs[spec], d.labelSpecs[spec]
                for k, v in pairs(ds) do
                    if ps[k] == nil then ps[k] = v end
                end
                if type(ps.color) ~= "table" then ps.color = ds.color end
            end
        end
    end

    -- Border
    if type(p.border) ~= "table" then
        p.border = d.border
    else
        for k, v in pairs(d.border) do
            if p.border[k] == nil then p.border[k] = v end
        end
        if type(p.border.color) ~= "table" then p.border.color = d.border.color end
    end

    -- Elemental markers
    if type(p.elemMarkers) ~= "table" then
        p.elemMarkers = d.elemMarkers
    else
        for i = 1, 4 do
            if type(p.elemMarkers[i]) ~= "table" then
                p.elemMarkers[i] = d.elemMarkers[i]
            else
                local m, dm = p.elemMarkers[i], d.elemMarkers[i]
                if m.enabled == nil then m.enabled = dm.enabled end
                if m.value   == nil then m.value   = dm.value   end
                if m.width   == nil then m.width   = dm.width   end
                if type(m.color) ~= "table" then m.color = dm.color end
            end
        end
    end

    if type(p.enhThresholds) ~= "table" then
        p.enhThresholds = d.enhThresholds
    else
        for i = 1, 3 do
            if type(p.enhThresholds[i]) ~= "table" then
                p.enhThresholds[i] = d.enhThresholds[i]
            else
                local t, dt = p.enhThresholds[i], d.enhThresholds[i]
                if t.enabled == nil then t.enabled = dt.enabled end
                if t.stacks  == nil then t.stacks  = dt.stacks  end
                if t.sound   == nil then t.sound   = dt.sound   end
                if type(t.color) ~= "table" then t.color = dt.color end
            end
        end
    end

    -- Remove stale keys from old DB versions
    local staleKeys = {
        "showLabel","labelFormat","labelSize","labelFont","labelColor",
        "labelOffsetX","labelOffsetY","elemThresholds","elemMax",
        "elemMarkerStyle","shadowEnabled","restThresholds",
    }
    for _, k in ipairs(staleKeys) do p[k] = nil end
end

-- ── Events ────────────────────────────────────────────────────────────
local f = CreateFrame("Frame")
f:RegisterEvent("PLAYER_LOGIN")
f:RegisterEvent("PLAYER_ENTERING_WORLD")
f:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED")
f:RegisterEvent("ACTIVE_TALENT_GROUP_CHANGED")
f:RegisterEvent("UNIT_AURA")
f:RegisterEvent("UNIT_POWER_UPDATE")
f:RegisterEvent("UNIT_POWER_FREQUENT")
f:RegisterEvent("PLAYER_REGEN_DISABLED")
f:RegisterEvent("PLAYER_REGEN_ENABLED")
f:RegisterEvent("PLAYER_MOUNT_DISPLAY_CHANGED")
f:RegisterEvent("UNIT_ENTERED_VEHICLE")
f:RegisterEvent("UNIT_EXITED_VEHICLE")
f:RegisterEvent("PET_BATTLE_OPENING_START")
f:RegisterEvent("PET_BATTLE_CLOSE")

f:SetScript("OnEvent", function(_, event, arg1, arg2)
    if event == "PLAYER_LOGIN" then
        InitDB()
        C_Timer.After(0.5, function()
            RefreshSpec()
            ApplySettings()
            BuildOptions()
            UpdateDisplay()
            ApplySeparators()
        end)
        -- Second pass: reapply font after UI is fully settled
        C_Timer.After(1.5, function()
            ApplyLabelFont()
            UpdateDisplay()
        end)

    elseif event == "PLAYER_ENTERING_WORLD" then
        C_Timer.After(0.3, function()
            RefreshSpec()
            ApplyLabelFont()
            ApplySeparators()
            UpdateDisplay()
        end)

    elseif event == "PLAYER_SPECIALIZATION_CHANGED"
        or event == "ACTIVE_TALENT_GROUP_CHANGED" then
        C_Timer.After(0.3, function()
            RefreshSpec()
            ApplyLabelFont()
            ApplySeparators()
            UpdateDisplay()
        end)

    elseif event == "UNIT_AURA" then
        if arg1 == "player" and currentSpec == "enhancement" then
            UpdateDisplay()
        end

    elseif event == "UNIT_POWER_UPDATE" or event == "UNIT_POWER_FREQUENT" then
        if arg1 == "player" then
            if currentSpec == "elemental" and (arg2 == "MAELSTROM" or arg2 == MAELSTROM_POWER) then
                UpdateDisplay()
            elseif currentSpec == "restoration" and (arg2 == "MANA" or arg2 == MANA_POWER) then
                UpdateDisplay()
            end
        end

    elseif event == "PLAYER_REGEN_DISABLED" or event == "PLAYER_REGEN_ENABLED" then
        UpdateDisplay()

    elseif event == "PLAYER_MOUNT_DISPLAY_CHANGED" then
        UpdateDisplay()

    elseif event == "UNIT_ENTERED_VEHICLE" or event == "UNIT_EXITED_VEHICLE" then
        if arg1 == "player" then UpdateDisplay() end

    elseif event == "PET_BATTLE_OPENING_START" or event == "PET_BATTLE_CLOSE" then
        UpdateDisplay()
    end
end)

-- ── Animation frame: smooth bar, spark, flash, Elemental poll ─────────
local pollTimer = 0
local animFrame = CreateFrame("Frame")
animFrame:SetScript("OnUpdate", function(_, elapsed)
    if not MRB_DB then return end
    local db = MRB_DB.profile

    -- Flash fade — runs regardless of spec so it always completes cleanly
    if flashAlpha > 0 then
        flashAlpha = math.max(0, flashAlpha - elapsed / FLASH_DURATION)
        flashTex:SetAlpha(flashAlpha)
        if flashAlpha == 0 then flashTex:Hide() end
    end

    if currentSpec == "enhancement" then
        -- Only run lerp while animation is in progress
        local animDone = (animElapsed >= ANIM_DURATION)
        if not animDone then
            animElapsed = math.min(animElapsed + elapsed, ANIM_DURATION)
            local t  = animElapsed / ANIM_DURATION
            local te = t * t * (3 - 2 * t)  -- smoothstep
            animCurrent = animFrom + (animTarget - animFrom) * te
            animDone = (animElapsed >= ANIM_DURATION)
            if animDone then animCurrent = animTarget end
        elseif animSettled then
            -- Fully settled, nothing to draw — skip all bar writes this frame
            return
        end
        animSettled = animDone  -- first frame animDone=true writes final state, then skips

        -- Color only recalculated when target changes (cached)
        if animTarget ~= animLastColorTarget then
            animLastColorTarget = animTarget
            animCachedR, animCachedG, animCachedB, animCachedA = GetEnhBarColor(animTarget, db)
        end

        -- 5+5 split mode
        if db.enhSplitMode then
            bar:SetMinMaxValues(0, 5)
            bar:SetValue(math.min(animCurrent, 5))
            bar:SetStatusBarColor(animCachedR, animCachedG, animCachedB, animCachedA)

            local overflow = animCurrent - 5
            if overflow > 0 then
                local c = db.enhSplitColor
                -- Sync bar2 background to bar bg so bar's fill doesn't bleed through
                local bgc = db.bgColor
                bar2Bg:SetColorTexture(bgc.r, bgc.g, bgc.b, bgc.a)
                bar2:SetMinMaxValues(0, 5)
                bar2:SetValue(math.min(overflow, 5))
                bar2:SetStatusBarColor(c.r, c.g, c.b, c.a)
                bar2:Show()
            else
                bar2Bg:SetColorTexture(0, 0, 0, 0)
                bar2:SetValue(0)
                bar2:Hide()
            end

            -- Spark: follows bar2 tip when overflow active, else bar tip
            local sparkVal = overflow > 0 and math.min(overflow, 5) or math.min(animCurrent, 5)
            if db.sparkEnabled and sparkVal > 0.05 and sparkVal < 5 then
                local pct = sparkVal / 5
                local w, h = barFrame:GetWidth(), barFrame:GetHeight()
                spark:ClearAllPoints()
                spark:SetPoint("CENTER", barFrame, "LEFT", pct * w, 0)
                spark:SetSize(8, h * 1.6)
                spark:Show()
            else
                spark:Hide()
            end
        else
            -- Normal 0-10 mode
            bar2:Hide()
            bar:SetMinMaxValues(0, 10)
            bar:SetValue(animCurrent)
            bar:SetStatusBarColor(animCachedR, animCachedG, animCachedB, animCachedA)
            if db.sparkEnabled and animCurrent > 0.05 and animCurrent < 10 then
                local pct = animCurrent / 10
                local w, h = barFrame:GetWidth(), barFrame:GetHeight()
                spark:ClearAllPoints()
                spark:SetPoint("CENTER", barFrame, "LEFT", pct * w, 0)
                spark:SetSize(8, h * 1.6)
                spark:Show()
            else
                spark:Hide()
            end
        end

    elseif currentSpec == "elemental" or currentSpec == "restoration" then
        -- Both specs use tainted primary resources; no lerp or spark possible.
        -- 0.25s poll keeps the bar current between events.
        spark:Hide()
        pollTimer = pollTimer + elapsed
        if pollTimer >= 0.25 then
            pollTimer = 0
            UpdateDisplay()
        end

    else
        spark:Hide()
        bar2:Hide()
    end
end)

-- ── Slash command ─────────────────────────────────────────────────────
SLASH_SRB1 = "/srb"
SlashCmdList["SRB"] = function(msg)
    msg = (msg or ""):lower():match("^%s*(.-)%s*$")
    if msg == "config" or msg == "" then
        local AceConfigDialog = LibStub and LibStub("AceConfigDialog-3.0", true)
        if AceConfigDialog then
            AceConfigDialog:Open("ShamanResourceBar")
        else
            InterfaceOptionsFrame_OpenToCategory("Shaman Resource Bar")
        end
    elseif msg == "reset" then
        MRB_DB.profile.posX = nil
        MRB_DB.profile.posY = nil
        ApplySettings()
        print("|cff00ccffSRB|r Position reset.")
    else
        print("|cff00ccffSRB|r  /srb — open options    /srb reset — reset position")
    end
end
