SFrames.MinimapBuffs = {}

local MB = SFrames.MinimapBuffs
local MAX_BUFFS = 32
local MAX_DEBUFFS = 16
local UPDATE_INTERVAL = 0.2

local BASE_X = -209
local BASE_Y = -26

--------------------------------------------------------------------------------
-- Buff Blacklist: Turtle WoW internal/proc buffs that should not appear
--------------------------------------------------------------------------------
local HIDDEN_BUFF_NAMES = {
    ["防御状态"] = true,
}

function SFrames:GetBuffName(buffIndex)
    if not SFrames.Tooltip then return nil end
    SFrames.Tooltip:ClearLines()
    SFrames.Tooltip:SetPlayerBuff(buffIndex)
    local nameObj = SFramesScanTooltipTextLeft1
    local name = nameObj and nameObj:GetText()
    SFrames.Tooltip:Hide()
    return name
end

function SFrames:IsBuffHidden(buffIndex)
    local name = self:GetBuffName(buffIndex)
    if not name or name == "" then return false end
    if HIDDEN_BUFF_NAMES[name] then return true end
    if SFramesDB and type(SFramesDB.hiddenBuffs) == "table" and SFramesDB.hiddenBuffs[name] then
        return true
    end
    return false
end

local ROUND_BACKDROP = {
    bgFile   = "Interface\\Tooltips\\UI-Tooltip-Background",
    edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
    tile = true, tileSize = 8, edgeSize = 8,
    insets = { left = 2, right = 2, top = 2, bottom = 2 },
}

local function GetDB()
    if not SFramesDB or type(SFramesDB.MinimapBuffs) ~= "table" then
        return {
            enabled = true, iconSize = 30, iconsPerRow = 8,
            spacing = 2, growDirection = "LEFT", position = "TOPRIGHT",
            offsetX = 0, offsetY = 0, showTimer = true,
            showDebuffs = true, debuffIconSize = 30,
        }
    end
    return SFramesDB.MinimapBuffs
end

local function FormatBuffTime(seconds)
    if not seconds or seconds <= 0 or seconds >= 99999 then
        return "N/A"
    end
    if seconds >= 3600 then
        local h = math.floor(seconds / 3600)
        local m = math.floor(math.mod(seconds, 3600) / 60)
        return h .. "h" .. m .. "m"
    elseif seconds >= 60 then
        return math.floor(seconds / 60) .. "m"
    else
        return math.floor(seconds) .. "s"
    end
end

local DEBUFF_TYPE_COLORS = {
    Magic   = { r = 0.20, g = 0.60, b = 1.00 },
    Curse   = { r = 0.60, g = 0.00, b = 1.00 },
    Disease = { r = 0.60, g = 0.40, b = 0.00 },
    Poison  = { r = 0.00, g = 0.60, b = 0.00 },
}
local DEBUFF_DEFAULT_COLOR = { r = 0.80, g = 0.00, b = 0.00 }
local WEAPON_ENCHANT_COLOR = { r = 0.58, g = 0.22, b = 0.82 }

local function HideBlizzardBuffs()
    for i = 0, 23 do
        local btn = _G["BuffButton" .. i]
        if btn then
            btn:Hide()
            btn:UnregisterAllEvents()
            btn.Show = function() end
        end
    end
    for i = 1, 3 do
        local te = _G["TempEnchant" .. i]
        if te then
            te:Hide()
            te:UnregisterAllEvents()
            te.Show = function() end
        end
    end
    if BuffFrame then
        BuffFrame:Hide()
        BuffFrame:UnregisterAllEvents()
        BuffFrame.Show = function() end
    end
    if TemporaryEnchantFrame then
        TemporaryEnchantFrame:Hide()
        TemporaryEnchantFrame:UnregisterAllEvents()
        TemporaryEnchantFrame.Show = function() end
    end
end

local function ApplySlotBackdrop(btn, isBuff)
    btn:SetBackdrop(ROUND_BACKDROP)
    btn:SetBackdropColor(0.06, 0.06, 0.08, 0.92)
    if isBuff then
        btn:SetBackdropBorderColor(0.25, 0.25, 0.30, 1)
    else
        local c = DEBUFF_DEFAULT_COLOR
        btn:SetBackdropBorderColor(c.r, c.g, c.b, 1)
    end
end

local function CreateSlot(parent, namePrefix, index, isBuff)
    local db = GetDB()
    local size = isBuff and (db.iconSize or 30) or (db.debuffIconSize or 30)

    local btn = CreateFrame("Button", namePrefix .. index, parent)
    btn:SetWidth(size)
    btn:SetHeight(size)
    ApplySlotBackdrop(btn, isBuff)

    btn.icon = btn:CreateTexture(nil, "ARTWORK")
    btn.icon:SetPoint("TOPLEFT", btn, "TOPLEFT", 2, -2)
    btn.icon:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", -2, 2)
    btn.icon:SetTexCoord(0.07, 0.93, 0.07, 0.93)

    btn.count = SFrames:CreateFontString(btn, 10, "RIGHT")
    btn.count:SetPoint("TOPRIGHT", btn, "TOPRIGHT", -1, -1)
    btn.count:SetTextColor(1, 1, 1)
    btn.count:SetShadowColor(0, 0, 0, 1)
    btn.count:SetShadowOffset(1, -1)

    btn.timer = SFrames:CreateFontString(btn, 9, "CENTER")
    btn.timer:SetPoint("BOTTOM", btn, "BOTTOM", 0, -11)
    btn.timer:SetTextColor(1, 0.82, 0)
    btn.timer:SetShadowColor(0, 0, 0, 1)
    btn.timer:SetShadowOffset(1, -1)

    btn.isBuff = isBuff
    btn:EnableMouse(true)
    btn:RegisterForClicks("RightButtonUp")

    btn:SetScript("OnEnter", function()
        if this._sfSimulated then
            GameTooltip:SetOwner(this, "ANCHOR_BOTTOMLEFT")
            GameTooltip:AddLine(this._sfSimLabel or "Simulated", 1, 1, 1)
            GameTooltip:AddLine(this._sfSimDesc or "", 0.7, 0.7, 0.7)
            GameTooltip:Show()
            return
        end
        if this._isWeaponEnchant and this._weaponSlotID then
            GameTooltip:SetOwner(this, "ANCHOR_BOTTOMLEFT")
            GameTooltip:SetInventoryItem("player", this._weaponSlotID)
            return
        end
        if this.buffIndex and this.buffIndex >= 0 then
            GameTooltip:SetOwner(this, "ANCHOR_BOTTOMLEFT")
            GameTooltip:SetPlayerBuff(this.buffIndex)
        end
    end)
    btn:SetScript("OnLeave", function()
        GameTooltip:Hide()
    end)
    btn:SetScript("OnClick", function()
        if this._sfSimulated then return end
        if this._isWeaponEnchant then return end
        if this.isBuff and this.buffIndex and this.buffIndex >= 0 then
            CancelPlayerBuff(this.buffIndex)
        end
    end)

    btn.buffIndex = -1
    btn._sfSimulated = false
    btn._isWeaponEnchant = false
    btn._weaponSlotID = nil
    btn:Hide()
    return btn
end

function MB:ApplyLayout()
    if not self.buffSlots then return end
    local db = GetDB()
    local size = db.iconSize or 30
    local spacing = db.spacing or 2
    local perRow = db.iconsPerRow or 8
    local growLeft = (db.growDirection == "LEFT")

    for i = 1, MAX_BUFFS do
        local btn = self.buffSlots[i]
        btn:SetWidth(size)
        btn:SetHeight(size)
        btn:ClearAllPoints()

        local col = math.mod(i - 1, perRow)
        local row = math.floor((i - 1) / perRow)

        local xDir = growLeft and -1 or 1
        local xOfs = col * (size + spacing) * xDir
        local yOfs = -row * (size + 14 + spacing)

        local anchor = growLeft and "TOPRIGHT" or "TOPLEFT"
        btn:SetPoint(anchor, self.buffContainer, anchor, xOfs, yOfs)
    end

    if not self.debuffSlots then return end
    local dSize = db.debuffIconSize or 30
    for i = 1, MAX_DEBUFFS do
        local btn = self.debuffSlots[i]
        btn:SetWidth(dSize)
        btn:SetHeight(dSize)
        btn:ClearAllPoints()

        local col = math.mod(i - 1, perRow)
        local row = math.floor((i - 1) / perRow)

        local xDir = growLeft and -1 or 1
        local xOfs = col * (dSize + spacing) * xDir
        local yOfs = -row * (dSize + 14 + spacing)

        local anchor = growLeft and "TOPRIGHT" or "TOPLEFT"
        btn:SetPoint(anchor, self.debuffContainer, anchor, xOfs, yOfs)
    end
end

local function CountVisibleRows(slots, maxSlots, perRow)
    local maxVisible = 0
    for i = 1, maxSlots do
        if slots[i]:IsShown() then maxVisible = i end
    end
    if maxVisible == 0 then return 0 end
    return math.floor((maxVisible - 1) / perRow) + 1
end

function MB:ApplyPosition()
    if not self.buffContainer then return end

    local saved = SFramesDB and SFramesDB.Positions and SFramesDB.Positions["MinimapBuffs"]
    if saved and saved.point and saved.relativePoint then
        self.buffContainer:ClearAllPoints()
        self.buffContainer:SetPoint(saved.point, UIParent, saved.relativePoint, saved.xOfs or 0, saved.yOfs or 0)
    else
        local db = GetDB()
        local pos = db.position or "TOPRIGHT"
        local x = BASE_X + (db.offsetX or 0)
        local y = BASE_Y + (db.offsetY or 0)
        self.buffContainer:ClearAllPoints()
        self.buffContainer:SetPoint(pos, UIParent, pos, x, y)
    end

    self:AnchorDebuffs()
end

function MB:AnchorDebuffs()
    if not self.debuffContainer or not self.buffContainer then return end
    local db = GetDB()
    local size = db.iconSize or 30
    local spacing = db.spacing or 2
    local perRow = db.iconsPerRow or 8
    local rows = CountVisibleRows(self.buffSlots, MAX_BUFFS, perRow)
    local rowHeight = size + 14 + spacing
    local gap = 6

    self.debuffContainer:ClearAllPoints()
    self.debuffContainer:SetPoint("TOPRIGHT", self.buffContainer, "TOPRIGHT", 0, -(rows * rowHeight + gap))
end

local function ApplyTimerColor(btn, timeText)
    if timeText == "N/A" then
        btn.timer:SetTextColor(0.6, 0.6, 0.6)
        return
    end
    local secs = nil
    local _, _, hVal = string.find(timeText, "(%d+)h")
    local _, _, mVal = string.find(timeText, "(%d+)m")
    local _, _, sVal = string.find(timeText, "(%d+)s")
    local h = tonumber(hVal)
    local m = tonumber(mVal)
    local s = tonumber(sVal)
    if h then
        secs = h * 3600 + (m or 0) * 60
    elseif m then
        secs = m * 60
    elseif s then
        secs = s
    end
    if secs and secs < 30 then
        btn.timer:SetTextColor(1, 0.3, 0.3)
    elseif secs and secs < 120 then
        btn.timer:SetTextColor(1, 0.82, 0)
    else
        btn.timer:SetTextColor(0.8, 1, 0.8)
    end
end

local function SetTimerFromSeconds(btn, timeLeft, untilCancelled, showTimer)
    if not showTimer then
        btn.timer:Hide()
        return
    end
    if untilCancelled == 1 or not timeLeft or timeLeft == 0 or timeLeft >= 99999 then
        btn.timer:SetText("N/A")
        btn.timer:SetTextColor(0.6, 0.6, 0.6)
    else
        btn.timer:SetText(FormatBuffTime(timeLeft))
        if timeLeft < 30 then
            btn.timer:SetTextColor(1, 0.3, 0.3)
        elseif timeLeft < 120 then
            btn.timer:SetTextColor(1, 0.82, 0)
        else
            btn.timer:SetTextColor(0.8, 1, 0.8)
        end
    end
    btn.timer:Show()
end

function MB:UpdateBuffs()
    if not self.buffSlots then return end
    if self._simulating then return end
    local db = GetDB()
    local showTimer = db.showTimer ~= false

    local slotIdx = 0
    for i = 0, 31 do
        local buffIndex, untilCancelled = GetPlayerBuff(i, "HELPFUL")
        if buffIndex and buffIndex >= 0 then
            if not SFrames:IsBuffHidden(buffIndex) then
                slotIdx = slotIdx + 1
                if slotIdx > MAX_BUFFS then break end

                local btn = self.buffSlots[slotIdx]
                local texture = GetPlayerBuffTexture(buffIndex)
                if texture then
                    btn.icon:SetTexture(texture)
                    btn.buffIndex = buffIndex
                    btn._sfSimulated = false
                    btn._isWeaponEnchant = false
                    btn._weaponSlotID = nil

                    local apps = GetPlayerBuffApplications(buffIndex)
                    if apps and apps > 1 then
                        btn.count:SetText(apps)
                        btn.count:Show()
                    else
                        btn.count:SetText("")
                        btn.count:Hide()
                    end

                    local timeLeft = GetPlayerBuffTimeLeft(buffIndex)
                    SetTimerFromSeconds(btn, timeLeft, untilCancelled, showTimer)

                    btn:SetBackdropBorderColor(0.25, 0.25, 0.30, 1)
                    btn:Show()
                else
                    btn:Hide()
                    btn.buffIndex = -1
                    btn._isWeaponEnchant = false
                    btn._weaponSlotID = nil
                end
            end
        end
    end

    local hasMainHandEnchant, mainHandExpiration, mainHandCharges,
          hasOffHandEnchant, offHandExpiration, offHandCharges = GetWeaponEnchantInfo()

    if hasMainHandEnchant then
        slotIdx = slotIdx + 1
        if slotIdx <= MAX_BUFFS then
            local btn = self.buffSlots[slotIdx]
            local texture = GetInventoryItemTexture("player", 16)
            if texture then
                btn.icon:SetTexture(texture)
                btn.buffIndex = -1
                btn._sfSimulated = false
                btn._isWeaponEnchant = true
                btn._weaponSlotID = 16

                if mainHandCharges and mainHandCharges > 0 then
                    btn.count:SetText(mainHandCharges)
                    btn.count:Show()
                else
                    btn.count:SetText("")
                    btn.count:Hide()
                end

                local timeLeft = mainHandExpiration and (mainHandExpiration / 1000) or 0
                SetTimerFromSeconds(btn, timeLeft, 0, showTimer)

                btn:SetBackdropBorderColor(WEAPON_ENCHANT_COLOR.r, WEAPON_ENCHANT_COLOR.g, WEAPON_ENCHANT_COLOR.b, 1)
                btn:Show()
            end
        end
    end

    if hasOffHandEnchant then
        slotIdx = slotIdx + 1
        if slotIdx <= MAX_BUFFS then
            local btn = self.buffSlots[slotIdx]
            local texture = GetInventoryItemTexture("player", 17)
            if texture then
                btn.icon:SetTexture(texture)
                btn.buffIndex = -1
                btn._sfSimulated = false
                btn._isWeaponEnchant = true
                btn._weaponSlotID = 17

                if offHandCharges and offHandCharges > 0 then
                    btn.count:SetText(offHandCharges)
                    btn.count:Show()
                else
                    btn.count:SetText("")
                    btn.count:Hide()
                end

                local timeLeft = offHandExpiration and (offHandExpiration / 1000) or 0
                SetTimerFromSeconds(btn, timeLeft, 0, showTimer)

                btn:SetBackdropBorderColor(WEAPON_ENCHANT_COLOR.r, WEAPON_ENCHANT_COLOR.g, WEAPON_ENCHANT_COLOR.b, 1)
                btn:Show()
            end
        end
    end

    for j = slotIdx + 1, MAX_BUFFS do
        local btn = self.buffSlots[j]
        btn:Hide()
        btn.buffIndex = -1
        btn._isWeaponEnchant = false
        btn._weaponSlotID = nil
    end

    self:UpdateDebuffs()
    self:AnchorDebuffs()
end

function MB:UpdateDebuffs()
    if not self.debuffSlots then return end
    if self._simulating then return end
    local db = GetDB()
    local showTimer = db.showTimer ~= false

    if db.showDebuffs == false then
        for i = 1, MAX_DEBUFFS do
            self.debuffSlots[i]:Hide()
        end
        if self.debuffContainer then self.debuffContainer:Hide() end
        return
    end
    if self.debuffContainer then self.debuffContainer:Show() end

    local slotIdx = 0
    for i = 0, 15 do
        local buffIndex, untilCancelled = GetPlayerBuff(i, "HARMFUL")
        if buffIndex and buffIndex >= 0 then
            slotIdx = slotIdx + 1
            if slotIdx > MAX_DEBUFFS then break end

            local btn = self.debuffSlots[slotIdx]
            local texture = GetPlayerBuffTexture(buffIndex)
            if texture then
                btn.icon:SetTexture(texture)
                btn.buffIndex = buffIndex
                btn._sfSimulated = false

                local apps = GetPlayerBuffApplications(buffIndex)
                if apps and apps > 1 then
                    btn.count:SetText(apps)
                    btn.count:Show()
                else
                    btn.count:SetText("")
                    btn.count:Hide()
                end

                local timeLeft = GetPlayerBuffTimeLeft(buffIndex)
                SetTimerFromSeconds(btn, timeLeft, untilCancelled, showTimer)

                local debuffType = nil
                SFrames.Tooltip:ClearLines()
                SFrames.Tooltip:SetPlayerBuff(buffIndex)
                local dTypeStr = SFramesScanTooltipTextRight1 and SFramesScanTooltipTextRight1:GetText()
                SFrames.Tooltip:Hide()
                if dTypeStr and dTypeStr ~= "" then debuffType = dTypeStr end

                local c = DEBUFF_TYPE_COLORS[debuffType] or DEBUFF_DEFAULT_COLOR
                btn:SetBackdropBorderColor(c.r, c.g, c.b, 1)

                btn:Show()
            else
                btn:Hide()
                btn.buffIndex = -1
            end
        end
    end

    for j = slotIdx + 1, MAX_DEBUFFS do
        self.debuffSlots[j]:Hide()
        self.debuffSlots[j].buffIndex = -1
    end
end

--------------------------------------------------------------------------------
-- Simulation (2 rows each for buff & debuff)
--------------------------------------------------------------------------------
local SIM_BUFFS = {
    -- Row 1
    { tex = "Interface\\Icons\\Spell_Holy_WordFortitude",           label = "Power Word: Fortitude",  desc = "Stamina +54",            time = "N/A" },
    { tex = "Interface\\Icons\\Spell_Shadow_AntiShadow",            label = "Shadow Protection",      desc = "Shadow Resistance +60",  time = "N/A" },
    { tex = "Interface\\Icons\\Spell_Holy_MagicalSentry",           label = "Arcane Intellect",       desc = "Intellect +31",          time = "42m" },
    { tex = "Interface\\Icons\\Spell_Nature_Regeneration",          label = "Mark of the Wild",       desc = "All stats +12",          time = "38m" },
    { tex = "Interface\\Icons\\Spell_Holy_GreaterBlessingofKings",  label = "Blessing of Kings",      desc = "All stats +10%",         time = "7m" },
    { tex = "Interface\\Icons\\Spell_Holy_PrayerOfHealing02",       label = "Renew",                  desc = "Heals 152 over 15 sec",  time = "12s" },
    { tex = "Interface\\Icons\\Spell_Holy_DivineSpirit",            label = "Divine Spirit",          desc = "Spirit +40",             time = "N/A" },
    { tex = "Interface\\Icons\\Spell_Fire_SealOfFire",              label = "Fire Shield",            desc = "Fire damage absorb",     time = "25m" },
    -- Row 2
    { tex = "Interface\\Icons\\Spell_Holy_PowerWordShield",         label = "Power Word: Shield",     desc = "Absorbs 942 damage",     time = "28s" },
    { tex = "Interface\\Icons\\Spell_Nature_Lightning",             label = "Lightning Shield",       desc = "3 charges",              time = "9m",  count = 3 },
    { tex = "Interface\\Icons\\Spell_Holy_SealOfWisdom",            label = "Blessing of Wisdom",     desc = "MP5 +33",                time = "5m" },
    { tex = "Interface\\Icons\\Spell_Nature_UndyingStrength",       label = "Thorns",                 desc = "Nature damage on hit",   time = "N/A" },
    { tex = "Interface\\Icons\\Spell_Nature_Invisibilty",           label = "Innervate",              desc = "Spirit +400%",           time = "18s" },
    { tex = "Interface\\Icons\\Spell_Holy_PowerInfusion",           label = "Power Infusion",         desc = "+20% spell damage",      time = "14s" },
    { tex = "Interface\\Icons\\Spell_Holy_SealOfValor",             label = "Blessing of Sanctuary",  desc = "Block damage reduced",   time = "3m" },
    { tex = "Interface\\Icons\\Spell_Nature_EnchantArmor",          label = "Nature Resistance",      desc = "Nature Resistance +60",  time = "1h12m" },
}

local SIM_DEBUFFS = {
    -- Row 1
    { tex = "Interface\\Icons\\Spell_Shadow_CurseOfTounable",      label = "Curse of Tongues",    desc = "Casting 50% slower",       time = "28s",  dtype = "Curse" },
    { tex = "Interface\\Icons\\Spell_Shadow_UnholyStrength",        label = "Weakened Soul",       desc = "Cannot be shielded",       time = "15s",  dtype = "Magic" },
    { tex = "Interface\\Icons\\Ability_Creature_Disease_02",        label = "Corrupted Blood",     desc = "Inflicts disease damage",  time = "N/A",  dtype = "Disease" },
    { tex = "Interface\\Icons\\Spell_Nature_CorrosiveBreath",       label = "Deadly Poison",       desc = "Inflicts Nature damage",   time = "8s",   dtype = "Poison" },
    { tex = "Interface\\Icons\\Spell_Shadow_Possession",            label = "Fear",                desc = "Feared for 8 sec",         time = "6s",   dtype = "Magic" },
    { tex = "Interface\\Icons\\Spell_Shadow_ShadowWordPain",        label = "Shadow Word: Pain",   desc = "Shadow damage over time",  time = "24s",  dtype = "Magic" },
    { tex = "Interface\\Icons\\Spell_Shadow_AbominationExplosion",  label = "Mortal Strike",       desc = "Healing reduced 50%",      time = "5s",   dtype = nil },
    { tex = "Interface\\Icons\\Spell_Frost_FrostArmor02",           label = "Frostbolt",           desc = "Movement slowed 40%",      time = "4s",   dtype = "Magic" },
    -- Row 2
    { tex = "Interface\\Icons\\Spell_Shadow_CurseOfSargeras",       label = "Curse of Agony",      desc = "Shadow damage over time",  time = "22s",  dtype = "Curse" },
    { tex = "Interface\\Icons\\Spell_Nature_Slow",                  label = "Crippling Poison",    desc = "Movement slowed 70%",      time = "10s",  dtype = "Poison" },
    { tex = "Interface\\Icons\\Spell_Shadow_CurseOfMannoroth",      label = "Curse of Elements",   desc = "Resistance reduced 75",    time = "N/A",  dtype = "Curse" },
    { tex = "Interface\\Icons\\Ability_Creature_Disease_03",        label = "Devouring Plague",    desc = "Disease damage + heal",    time = "20s",  dtype = "Disease" },
}

function MB:SimulateBuffs()
    if not self.buffSlots or not self.debuffSlots then return end
    self._simulating = true

    for i = 1, MAX_BUFFS do
        local btn = self.buffSlots[i]
        local sim = SIM_BUFFS[i]
        if sim then
            btn.icon:SetTexture(sim.tex)
            btn.buffIndex = -1
            btn._sfSimulated = true
            btn._isWeaponEnchant = false
            btn._weaponSlotID = nil
            btn._sfSimLabel = sim.label
            btn._sfSimDesc = sim.desc

            btn.timer:SetText(sim.time)
            ApplyTimerColor(btn, sim.time)
            btn.timer:Show()

            if sim.count and sim.count > 1 then
                btn.count:SetText(sim.count)
                btn.count:Show()
            else
                btn.count:Hide()
            end

            btn:SetBackdropBorderColor(0.25, 0.25, 0.30, 1)
            btn:Show()
        else
            btn:Hide()
        end
    end

    for i = 1, MAX_DEBUFFS do
        local btn = self.debuffSlots[i]
        local sim = SIM_DEBUFFS[i]
        if sim then
            btn.icon:SetTexture(sim.tex)
            btn.buffIndex = -1
            btn._sfSimulated = true
            btn._sfSimLabel = sim.label
            btn._sfSimDesc = sim.desc

            btn.timer:SetText(sim.time)
            ApplyTimerColor(btn, sim.time)
            btn.timer:Show()
            btn.count:Hide()

            local c = DEBUFF_TYPE_COLORS[sim.dtype] or DEBUFF_DEFAULT_COLOR
            btn:SetBackdropBorderColor(c.r, c.g, c.b, 1)
            btn:Show()
        else
            btn:Hide()
        end
    end

    if self.debuffContainer then self.debuffContainer:Show() end
    self:AnchorDebuffs()
end

function MB:StopSimulation()
    self._simulating = false
    self:UpdateBuffs()
end

function MB:Refresh()
    if not self.buffContainer then return end
    local db = GetDB()
    if db.enabled == false then
        self.buffContainer:Hide()
        if self.debuffContainer then self.debuffContainer:Hide() end
        return
    end
    self:ApplyPosition()
    self:ApplyLayout()
    if not self._simulating then
        self:UpdateBuffs()
    else
        self:AnchorDebuffs()
    end
    self.buffContainer:Show()
end

function MB:Initialize()
    local db = GetDB()
    if db.enabled == false then return end

    HideBlizzardBuffs()

    self.buffContainer = CreateFrame("Frame", "SFramesMBuffContainer", UIParent)
    self.buffContainer:SetWidth(400)
    self.buffContainer:SetHeight(200)
    self.buffContainer:SetFrameStrata("LOW")

    self.debuffContainer = CreateFrame("Frame", "SFramesMDebuffContainer", UIParent)
    self.debuffContainer:SetWidth(400)
    self.debuffContainer:SetHeight(100)
    self.debuffContainer:SetFrameStrata("LOW")

    self.buffSlots = {}
    for i = 1, MAX_BUFFS do
        self.buffSlots[i] = CreateSlot(self.buffContainer, "SFramesMBuff", i, true)
    end

    self.debuffSlots = {}
    for i = 1, MAX_DEBUFFS do
        self.debuffSlots[i] = CreateSlot(self.debuffContainer, "SFramesMDebuff", i, false)
    end

    self:ApplyPosition()
    self:ApplyLayout()

    self.updater = CreateFrame("Frame", nil, self.buffContainer)
    self.updater.timer = 0
    self.updater:SetScript("OnUpdate", function()
        this.timer = this.timer + arg1
        if this.timer >= UPDATE_INTERVAL then
            MB:UpdateBuffs()
            this.timer = 0
        end
    end)

    self:UpdateBuffs()

    if SFrames.Movers and SFrames.Movers.RegisterMover then
        if self.buffContainer then
            SFrames.Movers:RegisterMover("MinimapBuffs", self.buffContainer, "Buff",
                "TOPRIGHT", "UIParent", "TOPRIGHT", BASE_X + (db.offsetX or 0), BASE_Y + (db.offsetY or 0),
                function() MB:AnchorDebuffs() end)
        end
        if self.debuffContainer then
            SFrames.Movers:RegisterMover("MinimapDebuffs", self.debuffContainer, "Debuff",
                "TOPRIGHT", "UIParent", "TOPRIGHT", BASE_X + (db.offsetX or 0), BASE_Y - 50)
        end
    end
end
