local addonName, ns = ...

-- ============================================================================
-- BrightQoL – Game Bar
-- Icons: WindTools-style TGA files from Assets/GameBar/
-- Floating 3-panel bar: left buttons + clock + right buttons.
-- ============================================================================

local GameBar = {}
ns.GameBar = GameBar

local function DB() return BrightQoL.gameBar end

local function GetBorderColor(db)
    if not db then return 0, 207/255, 1.0 end
    if db.borderUseClassColor then
        local cc = RAID_CLASS_COLORS and RAID_CLASS_COLORS[select(2, UnitClass("player"))]
        if cc then return cc.r, cc.g, cc.b end
    end
    local r = db.borderColorR or 0
    local g = db.borderColorG or (207/255)
    local b = db.borderColorB or 1.0
    return r, g, b
end


local CYAN_R, CYAN_G, CYAN_B = 0, 207/255, 1.0
local GB = "Interface\\AddOns\\BrightQoL\\Assets\\GameBar\\"

local BACKDROP = {
    bgFile   = "Interface\\Buttons\\WHITE8x8",
    edgeFile = "Interface\\Buttons\\WHITE8x8",
    edgeSize = 1,
    insets   = { left = 1, right = 1, top = 1, bottom = 1 },
}

local function MakeBackdrop(f, bgA)
    f:SetBackdrop(BACKDROP)
    f:SetBackdropColor(0.05, 0.05, 0.08, bgA or 0.88)
    f:SetBackdropBorderColor(CYAN_R, CYAN_G, CYAN_B, 0.6)
end

-- Apply backdrop/border visibility to a panel based on current settings
local function ApplyPanelVisuals(f, forceShowBg)
    if not f or not f.SetBackdropColor then return end
    local db = DB()
    local showBg  = forceShowBg or db.showBackdrop
    local showBrd = db.showBorder
    f:SetBackdropColor(0.05, 0.05, 0.08, showBg and 0.88 or 0.0)
    do local _r,_g,_b = GetBorderColor(DB()); f:SetBackdropBorderColor(_r,_g,_b, showBrd and 0.6 or 0.0) end
end

-- ── Blizzard bar hide (SetAlpha to avoid taint in combat lockdown) ─────────────
local BLIZZ_FRAMES = { "MicroButtonAndBagsBar", "MicroMenu", "MicroMenuContainer", "BagsBar" }
local blizzHidden = false
local pendingBlizzHide = false
local pendingBlizzShow = false

local function SetMouseEnabled(frame, enabled)
    pcall(function() frame:EnableMouse(enabled) end)
    for _, child in ipairs({ frame:GetChildren() }) do
        SetMouseEnabled(child, enabled)
    end
end

local function HideBlizzBar()
    if blizzHidden then return end
    blizzHidden = true
    for _, name in ipairs(BLIZZ_FRAMES) do
        local f = _G[name]
        if f then
            f:SetAlpha(0)
            SetMouseEnabled(f, false)
            if not f.__brightBlizzHooked then
                f.__brightBlizzHooked = true
                f:HookScript("OnShow", function(self)
                    if blizzHidden then
                        self:SetAlpha(0)
                        SetMouseEnabled(self, false)
                    end
                end)
            end
        end
    end
end

local function ShowBlizzBar()
    if not blizzHidden then return end
    blizzHidden = false
    for _, name in ipairs(BLIZZ_FRAMES) do
        local f = _G[name]
        if f then
            f:SetAlpha(1)
            SetMouseEnabled(f, true)
        end
    end
end

local function ApplyBlizzBarState()
    if DB().hideBlizzBar then HideBlizzBar() else ShowBlizzBar() end
end

-- ── Pixel-perfect helpers ────────────────────────────────────────────────────
-- WoW renders at various UI scales. A 26px frame at scale 0.85 = 22.1 real
-- pixels — the 1px edgeFile border falls on a fractional pixel and vanishes.
-- PixelSnap rounds to the nearest physical pixel boundary at current scale.

local function GetPixelSize()
    local physW = GetPhysicalScreenSize()
    local screenW = GetScreenWidth()
    local scale   = UIParent:GetEffectiveScale()
    if physW and physW > 0 and screenW and screenW > 0 and scale and scale > 0 then
        return screenW / physW / scale
    end
    return 1 / scale  -- fallback
end

local function PixelSnap(value)
    local ps = GetPixelSize()
    return math.floor(value / ps + 0.5) * ps
end
local function LuaClick(btn, fn1, fn2)
    btn:SetScript("OnClick", function(self, mouse)
        if mouse == "RightButton" and fn2 then fn2()
        else fn1() end
    end)
end

local function MacroAttr(btn, m1, m2)
    btn:SetAttribute("type*",      "macro")
    btn:SetAttribute("macrotext1", m1 or "")
    btn:SetAttribute("macrotext2", m2 or m1 or "")
end

local function Tip(btn, title, lines)
    btn:SetScript("OnEnter", function(self)
        GameTooltip:SetOwner(self, "ANCHOR_TOP", 0, 4)
        GameTooltip:ClearLines()
        GameTooltip:SetText(title, CYAN_R, CYAN_G, CYAN_B)
        if lines then for _, l in ipairs(lines) do GameTooltip:AddLine(l,1,1,1) end end
        GameTooltip:Show()
        if DB().showBorder then self:SetBackdropBorderColor(1,1,1,0.9) end
    end)
    btn:SetScript("OnLeave", function(self)
        GameTooltip:Hide()
        do local _r,_g,_b = GetBorderColor(DB()); self:SetBackdropBorderColor(_r,_g,_b, DB().showBorder and 0.6 or 0.0) end
    end)
end

-- ── Button type definitions ───────────────────────────────────────────────────
local BTN_DEFS = {}
GameBar.BTN_DEFS  = BTN_DEFS
GameBar.SLOT_COUNT = 5

BTN_DEFS["CHARACTER"] = { name = "Character", build = function(btn)
    btn.iconTex:SetTexture(GB.."Character.tga")
    LuaClick(btn, function() if not InCombatLockdown() then ToggleCharacter("PaperDollFrame") end end)
    Tip(btn, "Character", {"Open character sheet"})
end}

BTN_DEFS["SPELLBOOK"] = { name = "Spellbook", build = function(btn)
    btn.iconTex:SetTexture(GB.."SpellBook.tga")
    LuaClick(btn, function()
        if not InCombatLockdown() then
            if PlayerSpellsUtil and PlayerSpellsUtil.TogglePlayerSpellsFrame then
                PlayerSpellsUtil.TogglePlayerSpellsFrame()
            elseif ToggleSpellBook then
                ToggleSpellBook("spellbook")
            end
        end
    end)
    Tip(btn, "Spellbook", {"Open spellbook"})
end}

BTN_DEFS["TALENTS"] = { name = "Talents", build = function(btn)
    btn.iconTex:SetTexture(GB.."Talents.tga")
    LuaClick(btn, function()
        if not InCombatLockdown() then
            if PlayerSpellsUtil then PlayerSpellsUtil.ToggleClassTalentFrame()
            elseif ToggleTalentFrame then ToggleTalentFrame() end
        end
    end)
    Tip(btn, "Talents", {"Open talent frame"})
end}

BTN_DEFS["ACHIEVEMENTS"] = { name = "Achievements", build = function(btn)
    btn.iconTex:SetTexture(GB.."Achievements.tga")
    LuaClick(btn, function()
        if not InCombatLockdown() then
            if ToggleAchievementFrame then ToggleAchievementFrame() end
        end
    end)
    Tip(btn, "Achievements", {"Open achievements"})
end}

BTN_DEFS["QUEST_LOG"] = { name = "Quest Log", build = function(btn)
    btn.iconTex:SetTexture(GB.."QuestLog.tga")
    LuaClick(btn, function()
        if not InCombatLockdown() then
            if ToggleQuestLog then ToggleQuestLog() end
        end
    end)
    Tip(btn, "Quest Log", {"Open quest log"})
end}

BTN_DEFS["GROUP_FINDER"] = { name = "Group Finder", build = function(btn)
    btn.iconTex:SetTexture(GB.."GroupFinder.tga")
    LuaClick(btn, function()
        if not InCombatLockdown() then
            if PVEFrame_ToggleFrame then PVEFrame_ToggleFrame()
            elseif ToggleLFDParentFrame then ToggleLFDParentFrame() end
        end
    end)
    Tip(btn, "Group Finder", {"Open Group Finder"})
end}

BTN_DEFS["GUILD"] = { name = "Guild", build = function(btn)
    btn.iconTex:SetTexture(GB.."Guild.tga")
    LuaClick(btn, function()
        if not InCombatLockdown() then
            if ToggleGuildFrame then ToggleGuildFrame()
            elseif GuildFrame_Toggle then GuildFrame_Toggle() end
        end
    end)
    Tip(btn, "Guild", {"Open guild panel"})
    btn:RegisterEvent("GUILD_ROSTER_UPDATE"); btn:RegisterEvent("PLAYER_GUILD_UPDATE")
    btn:SetScript("OnEvent", function()
        if btn.countText and IsInGuild then
            local _, online = GetNumGuildMembers()
            btn.countText:SetText(online and online > 0 and tostring(online) or "")
        end
    end)
    C_Timer.After(2, function()
        if btn.countText and IsInGuild and IsInGuild() then
            local _, online = GetNumGuildMembers()
            btn.countText:SetText(online and online > 0 and tostring(online) or "")
        end
    end)
end}

BTN_DEFS["COLLECTIONS"] = { name = "Collections", build = function(btn)
    btn.iconTex:SetTexture(GB.."Collections.tga")
    LuaClick(btn,
        function()
            if not InCombatLockdown() then
                if ToggleCollectionsJournal then ToggleCollectionsJournal(1) end
            end
        end,
        function() C_MountJournal.SummonByID(0) end)
    Tip(btn, "Collections", {"Left: Collections", "Right: Random Mount"})
end}

BTN_DEFS["ENCOUNTER_JOURNAL"] = { name = "Encounter Journal", build = function(btn)
    btn.iconTex:SetTexture(GB.."EncounterJournal.tga")
    LuaClick(btn,
        function()
            if not InCombatLockdown() then
                if ToggleEncounterJournal then ToggleEncounterJournal() end
            end
        end,
        function()
            if WeeklyRewards_ShowUI then WeeklyRewards_ShowUI() end
        end)
    Tip(btn, "Encounter Journal", {"Left: Encounter Journal", "Right: Weekly Rewards"})
end}

BTN_DEFS["PET_JOURNAL"] = { name = "Pet Journal", build = function(btn)
    btn.iconTex:SetTexture(GB.."PetJournal.tga")
    LuaClick(btn,
        function()
            if not InCombatLockdown() then
                if ToggleCollectionsJournal then ToggleCollectionsJournal(2) end
            end
        end,
        function() C_PetJournal.SummonRandomPet(C_PetJournal.HasFavoritePets()) end)
    Tip(btn, "Pet Journal", {"Left: Pet Journal", "Right: Random Pet"})
end}

BTN_DEFS["TOY_BOX"] = { name = "Toy Box", build = function(btn)
    btn.iconTex:SetTexture(GB.."ToyBox.tga")
    LuaClick(btn, function()
        if not InCombatLockdown() then
            if ToggleCollectionsJournal then ToggleCollectionsJournal(3) end
        end
    end)
    Tip(btn, "Toy Box", {"Open Toy Box"})
end}

BTN_DEFS["BAGS"] = { name = "Bags", build = function(btn)
    btn.iconTex:SetTexture(GB.."Bags.tga")
    LuaClick(btn, function()
        if not InCombatLockdown() then
            ToggleAllBags()
        end
    end)
    Tip(btn, "Bags", {"Open all bags"})
end}

BTN_DEFS["FRIENDS"] = { name = "Friends", build = function(btn)
    btn.iconTex:SetTexture(GB.."Friends.tga")
    LuaClick(btn, function()
        ToggleFriendsFrame(1)
    end)
    Tip(btn, "Friends", {"Open friends list"})
    btn:RegisterEvent("FRIENDLIST_UPDATE")
    btn:SetScript("OnEvent", function()
        if btn.countText and C_FriendList then
            local n = C_FriendList.GetNumOnlineFriends and C_FriendList.GetNumOnlineFriends() or 0
            btn.countText:SetText(n > 0 and tostring(n) or "")
        end
    end)
end}

BTN_DEFS["PROFESSION"] = { name = "Profession", build = function(btn)
    btn.iconTex:SetTexture(GB.."Profession.tga")
    LuaClick(btn, function() if not InCombatLockdown() and ToggleProfessionsBook then ToggleProfessionsBook() end end)
    Tip(btn, "Profession", {"Open profession book"})
end}

BTN_DEFS["GAME_MENU"] = { name = "Game Menu", build = function(btn)
    btn.iconTex:SetTexture(GB.."GameMenu.tga")
    LuaClick(btn, function()
        if not InCombatLockdown() then
            if GameMenuFrame:IsShown() then PlaySound(854); HideUIPanel(GameMenuFrame)
            else PlaySound(850); ShowUIPanel(GameMenuFrame) end
        end
    end)
    Tip(btn, "Game Menu", {"Open game menu"})
end}

BTN_DEFS["SCREENSHOT"] = { name = "Screenshot", build = function(btn)
    btn.iconTex:SetTexture(GB.."ScreenShot.tga")
    LuaClick(btn,
        function() GameTooltip:Hide(); Screenshot() end,
        function() C_Timer.After(2, Screenshot) end)
    Tip(btn, "Screenshot", {"Left: Now", "Right: In 2 seconds"})
end}

BTN_DEFS["VOLUME"] = { name = "Volume", build = function(btn)
    btn.iconTex:SetTexture(GB.."Volume.tga")
    btn:EnableMouseWheel(true)
    btn:SetScript("OnMouseWheel", function(_, d)
        local v = tonumber(C_CVar.GetCVar("Sound_MasterVolume")) or 0
        C_CVar.SetCVar("Sound_MasterVolume", ("%.2f"):format(math.max(0, math.min(1, v + d*0.05))))
    end)
    LuaClick(btn,
        function() local v=tonumber(C_CVar.GetCVar("Sound_MasterVolume")) or 0; C_CVar.SetCVar("Sound_MasterVolume",("%.2f"):format(math.min(1,v+0.1))) end,
        function() local v=tonumber(C_CVar.GetCVar("Sound_MasterVolume")) or 0; C_CVar.SetCVar("Sound_MasterVolume",("%.2f"):format(math.max(0,v-0.1))) end)
    Tip(btn, "Volume", {"Left: +10%  Right: -10%", "Scroll: fine tune"})
end}

BTN_DEFS["HEARTHSTONE"] = { name = "Hearthstone", build = function(btn)
    btn.iconTex:SetTexture(GB.."Hearthstone.tga")

    -- Secure frame parented to UIParent to avoid any bar taint
    local sb = CreateFrame("Button", nil, UIParent, "SecureActionButtonTemplate")
    sb:SetPoint("TOPLEFT", btn, "TOPLEFT", 0, 0)
    sb:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", 0, 0)
    sb:EnableMouse(true)
    sb:RegisterForClicks("AnyDown", "AnyUp")
    sb:SetFrameStrata("HIGH")
    sb:SetFrameLevel(100)
    sb:SetAttribute("type", "item")
    sb:SetAttribute("item", "item:140192")

    sb:SetScript("PreClick", function(self, mouseButton)
        if mouseButton == "LeftButton"   then self:SetAttribute("type","item"); self:SetAttribute("item","item:140192")
        elseif mouseButton == "RightButton"  then self:SetAttribute("type","macro"); self:SetAttribute("macrotext","/use Hearthstone")
        elseif mouseButton == "MiddleButton" then self:SetAttribute("type","item"); self:SetAttribute("item","item:253629")
        end
    end)

    sb:SetScript("OnEnter", function() local e = btn:GetScript("OnEnter"); if e then e(btn) end end)
    sb:SetScript("OnLeave", function() local e = btn:GetScript("OnLeave"); if e then e(btn) end end)

    Tip(btn, "Hearthstone", {
        "Left: Dalaran Hearthstone",
        "Right: Hearthstone",
        "Middle: Personal Key to the Arcantina",
    })
end}
BTN_DEFS["NONE"] = { name = "— None —", build = function(btn)
    if btn.iconTex then btn.iconTex:Hide() end
    btn:Hide()
end}

-- ── Frame refs ─────────────────────────────────────────────────────────────────
local bar, leftPanel, centerPanel, rightPanel = nil, nil, nil, nil
local buttons = {}
local SLOT_COUNT = 5

-- ── Create a slot button ───────────────────────────────────────────────────────
local function CreateSlot(parent)
    local sz = DB().buttonSize
    local f  = CreateFrame("Button", nil, parent, "SecureActionButtonTemplate, BackdropTemplate")
    f:SetSize(sz, sz)
    MakeBackdrop(f, 0.0)
    f:RegisterForClicks("AnyUp")
    f:EnableMouseWheel(true)

    local tex = f:CreateTexture(nil, "ARTWORK")
    tex:SetAllPoints()
    f.iconTex = tex

    local ct = f:CreateFontString(nil, "OVERLAY")
    ct:SetFont("Fonts\\FRIZQT__.TTF", 9, "OUTLINE")
    ct:SetPoint("BOTTOMRIGHT", 1, -1)
    ct:SetTextColor(CYAN_R, CYAN_G, CYAN_B, 1)
    f.countText = ct

    return f
end

local function ApplySlotType(btn, typeKey)
    btn:SetScript("OnClick", nil)
    btn:SetScript("OnEnter", nil)
    btn:SetScript("OnLeave", nil)
    btn:SetScript("OnMouseWheel", nil)
    btn:SetScript("OnEvent", nil)
    btn:UnregisterAllEvents()
    btn:ClearAttributes()
    if btn.iconTex then btn.iconTex:Show(); btn.iconTex:SetTexCoord(0,1,0,1); btn.iconTex:SetVertexColor(1,1,1,1); btn.iconTex:SetTexture(nil) end
    if btn.countText then btn.countText:SetText("") end
    btn:RegisterForClicks("AnyUp")

    if typeKey == "NONE" or not typeKey then btn:Hide(); return end
    local def = BTN_DEFS[typeKey] or BTN_DEFS["NONE"]
    btn:Show()
    def.build(btn)

    -- Wrap tooltips for mouseover fade
    local origEnter = btn:GetScript("OnEnter")
    local origLeave = btn:GetScript("OnLeave")
    btn:SetScript("OnEnter", function(self)
        if origEnter then origEnter(self) end
        if DB().mouseOver and bar then bar:SetAlpha(DB().alpha) end
    end)
    btn:SetScript("OnLeave", function(self)
        if origLeave then origLeave(self) end
        if DB().mouseOver and bar then bar:SetAlpha(0) end
    end)
end

-- ── Layout ──────────────────────────────────────────────────────────────────────
local function LayoutPanel(panel, slots, types)
    local sz  = PixelSnap(DB().buttonSize)
    local sp  = PixelSnap(DB().spacing)
    local pad = PixelSnap(DB().padding)
    local vis = 0; local prev = nil

    for i, btn in ipairs(slots) do
        local tk = types[i] or "NONE"
        ApplySlotType(btn, tk)
        btn:SetSize(sz, sz)
        if tk ~= "NONE" then
            btn:ClearAllPoints()
            if not prev then btn:SetPoint("LEFT", panel, "LEFT", pad, 0)
            else              btn:SetPoint("LEFT", prev, "RIGHT", sp, 0) end
            prev = btn; vis = vis + 1
        end
    end

    if vis == 0 then panel:Hide(); return 0 end
    panel:SetSize(PixelSnap(pad*2 + vis*sz + (vis-1)*sp), PixelSnap(sz + pad*2))
    panel:Show()
    return panel:GetWidth()
end

local function LayoutBar()
    if not bar then return end
    local db  = DB()
    local sz  = PixelSnap(db.buttonSize)
    local cw  = PixelSnap(db.clockWidth)
    local ch  = PixelSnap(db.clockHeight)

    local lSlots = {}; for i=1,SLOT_COUNT do lSlots[i] = buttons[i]          end
    local rSlots = {}; for i=1,SLOT_COUNT do rSlots[i] = buttons[i+SLOT_COUNT] end

    LayoutPanel(leftPanel,  lSlots, db.left)
    LayoutPanel(rightPanel, rSlots, db.right)
    centerPanel:SetSize(PixelSnap(cw), PixelSnap(ch))
    centerPanel:Show()

    local lw   = leftPanel:IsShown()  and leftPanel:GetWidth()  or 0
    local rw   = rightPanel:IsShown() and rightPanel:GetWidth() or 0
    local gapL = lw > 0 and 6 or 0
    local gapR = rw > 0 and 6 or 0

    leftPanel:ClearAllPoints();  leftPanel:SetPoint("RIGHT",  centerPanel, "LEFT",  -gapL, 0)
    rightPanel:ClearAllPoints(); rightPanel:SetPoint("LEFT",  centerPanel, "RIGHT",  gapR, 0)

    local totalW = PixelSnap(lw + gapL + cw + gapR + rw)
    local totalH = PixelSnap(math.max(sz + db.padding*2, ch))
    bar:SetSize(totalW, totalH)
end

-- ── Clock ───────────────────────────────────────────────────────────────────────
local function UpdateClock()
    if not centerPanel or not centerPanel.timeTxt then return end
    local db = DB()
    if db.localTime then
        centerPanel.timeTxt:SetText(date(db.twentyFour and "%H:%M" or "%I:%M"))
    else
        local h, m = GetGameTime()
        if not db.twentyFour then h = h % 12; if h == 0 then h = 12 end end
        centerPanel.timeTxt:SetFormattedText("%02d:%02d", h, m)
    end
    centerPanel.dateTxt:SetText(date("%d %b"))
end

local function BuildClock()
    if centerPanel.timeTxt then return end
    local tt = centerPanel:CreateFontString(nil, "OVERLAY")
    tt:SetFont("Fonts\\FRIZQT__.TTF", 20, "OUTLINE")
    tt:SetPoint("CENTER", 0, 6)
    tt:SetTextColor(1,1,1,1)
    centerPanel.timeTxt = tt

    local dt = centerPanel:CreateFontString(nil, "OVERLAY")
    dt:SetFont("Fonts\\FRIZQT__.TTF", 10, "OUTLINE")
    dt:SetPoint("CENTER", 0, -10)
    dt:SetTextColor(CYAN_R, CYAN_G, CYAN_B, 0.8)
    centerPanel.dateTxt = dt

    centerPanel:EnableMouse(true)
    centerPanel:RegisterForClicks("AnyUp")
    centerPanel:SetScript("OnClick", function(_, mouse)
        if mouse == "RightButton" then ToggleTimeManager()
        elseif not InCombatLockdown() then ToggleCalendar() end
    end)
    centerPanel:SetScript("OnEnter", function(self)
        if DB().mouseOver and bar then bar:SetAlpha(DB().alpha) end
        GameTooltip:SetOwner(self,"ANCHOR_TOP",0,4)
        GameTooltip:ClearLines()
        GameTooltip:SetText("Clock", CYAN_R,CYAN_G,CYAN_B)
        GameTooltip:AddLine("Left: Calendar  Right: Time Manager",1,1,1)
        GameTooltip:Show()
        -- Only highlight border if show border is on
        if DB().showBorder then
            self:SetBackdropBorderColor(1,1,1,0.8)
            if self._bottomLine then self._bottomLine:SetVertexColor(1,1,1,0.8) end
        end
    end)
    centerPanel:SetScript("OnLeave", function(self)
        if DB().mouseOver and bar then bar:SetAlpha(0) end
        GameTooltip:Hide()
        -- Restore to correct state based on setting
        local showBrd = DB().showBorder
        self:SetBackdropBorderColor(CYAN_R,CYAN_G,CYAN_B, showBrd and 0.6 or 0.0)
        if self._bottomLine then
            self._bottomLine:SetVertexColor(CYAN_R,CYAN_G,CYAN_B, showBrd and 0.6 or 0.0)
        end
    end)

    -- Explicit bottom border line. Positioned 1px above the absolute bottom
    -- edge so it isn't clipped/overdrawn by the parent bar frame's own border.
    local bottomLine = centerPanel:CreateTexture(nil, "BORDER")
    bottomLine:SetColorTexture(CYAN_R, CYAN_G, CYAN_B, 0.6)
    bottomLine:SetHeight(1)
    bottomLine:SetPoint("BOTTOMLEFT",  centerPanel, "BOTTOMLEFT",  1, 1)
    bottomLine:SetPoint("BOTTOMRIGHT", centerPanel, "BOTTOMRIGHT", -1, 1)
    centerPanel._bottomLine = bottomLine

    UpdateClock()
    C_Timer.NewTicker(10, UpdateClock)
end

-- ── Build bar ────────────────────────────────────────────────────────────────────
local function BuildBar()
    if bar then return end
    local db = DB()

    bar = CreateFrame("Frame","BrightQoL_GameBar",UIParent,"BackdropTemplate")
    bar:SetMovable(true); bar:EnableMouse(true); bar:SetClampedToScreen(true)
    bar:SetFrameStrata("MEDIUM"); bar:RegisterForDrag("LeftButton")
    bar:SetScript("OnDragStart", function(self) if not DB().locked then self:StartMoving() end end)
    bar:SetScript("OnDragStop",  function(self)
        self:StopMovingOrSizing()
        local pt,_,_,x,y = self:GetPoint()
        BrightQoL.gameBar.pos = {pt,x,y}
    end)
    bar:SetScript("OnEnter", function() if DB().mouseOver then bar:SetAlpha(DB().alpha) end end)
    bar:SetScript("OnLeave", function() if DB().mouseOver then bar:SetAlpha(0) end end)

    local p = db.pos
    if p then bar:SetPoint(p[1],UIParent,p[1],p[2],p[3])
    else      bar:SetPoint("TOP",UIParent,"TOP",0,-4) end

    leftPanel   = CreateFrame("Frame",nil,bar,"BackdropTemplate"); MakeBackdrop(leftPanel)
    leftPanel:SetPoint("CENTER",bar,"CENTER",0,0)
    centerPanel = CreateFrame("Button",nil,bar,"BackdropTemplate"); MakeBackdrop(centerPanel)
    centerPanel:SetPoint("CENTER",bar,"CENTER",0,0)
    rightPanel  = CreateFrame("Frame",nil,bar,"BackdropTemplate"); MakeBackdrop(rightPanel)
    rightPanel:SetPoint("CENTER",bar,"CENTER",0,0)

    for i = 1, SLOT_COUNT*2 do
        buttons[i] = CreateSlot(i <= SLOT_COUNT and leftPanel or rightPanel)
    end

    BuildClock()
end

-- ── Public API ────────────────────────────────────────────────────────────────────
function GameBar:ApplySettings()
    if not bar then return end
    bar:SetAlpha(DB().mouseOver and 0 or DB().alpha)
    LayoutBar()
    -- Backdrop/border visibility
    ApplyPanelVisuals(leftPanel)
    ApplyPanelVisuals(rightPanel)
    ApplyPanelVisuals(centerPanel)
    -- Sync explicit bottom border line on clock panel
    if centerPanel and centerPanel._bottomLine then
        local db = DB()
        do local _r,_g,_b = GetBorderColor(db); centerPanel._bottomLine:SetVertexColor(_r,_g,_b, db.showBorder and 0.6 or 0.0) end
    end
    -- Also update slot button borders
    for _, btn in ipairs(buttons) do
        if btn and btn.SetBackdropBorderColor then
            local db = DB()
            do local _r,_g,_b = GetBorderColor(DB()); btn:SetBackdropBorderColor(_r,_g,_b, db.showBorder and 0.6 or 0.0) end
            btn:SetBackdropColor(0.05, 0.05, 0.08, db.showBackdrop and 0.88 or 0.0)
        end
    end
end

function GameBar:UpdateVisibility()
    if not bar then return end
    local db = DB()
    if db.enabled then
        bar:Show()
    else
        bar:Hide()
    end
end

function GameBar:ApplyBlizzBar()
    ApplyBlizzBarState()
end

function GameBar:Refresh()
    GameBar:ApplySettings()
    GameBar:UpdateVisibility()
    UpdateClock()
end

-- ── Init ────────────────────────────────────────────────────────────────────────
local initFrame = CreateFrame("Frame")
initFrame:RegisterEvent("PLAYER_LOGIN")
initFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
initFrame:SetScript("OnEvent", function(self, event)
    if event == "PLAYER_LOGIN" then
        self:UnregisterEvent("PLAYER_LOGIN")
        BuildBar()
        GameBar:ApplySettings()
        GameBar:UpdateVisibility()
        ApplyBlizzBarState()
    elseif event == "PLAYER_REGEN_ENABLED" then
        if pendingBlizzHide then
            pendingBlizzHide = false
            HideBlizzBar()
        elseif pendingBlizzShow then
            pendingBlizzShow = false
            ShowBlizzBar()
        end
    end
end)
