-------------------------------------------------------------------------------
-- Project: AscensionBars
-- Author: Aka-DoctorCode
-- File: Config.lua
-- Version: V32
-------------------------------------------------------------------------------
-- Copyright (c) 2025–2026 Aka-DoctorCode. All Rights Reserved.
--
-- This software and its source code are the exclusive property of the author.
-- No part of this file may be copied, modified, redistributed, or used in
-- derivative works without express written permission.
-------------------------------------------------------------------------------

local addonName, _ = ...
local ascensionBars = LibStub("AceAddon-3.0"):GetAddon(addonName)
local Locales = LibStub("AceLocale-3.0"):GetLocale(addonName)
local colors = ascensionBars.colors
local files = ascensionBars.files
local menuStyle = ascensionBars.menuStyle

-------------------------------------------------------------------------------
-- UTILS
-------------------------------------------------------------------------------

local function cleanOrders(profile, blockField, orderField, block)
    local temp = {}
    for k, bar in pairs(profile.bars) do
        if bar[blockField] == block then
            table.insert(temp, { key = k, order = bar[orderField] or 99 })
        end
    end
    table.sort(temp, function(a, b) return a.order < b.order end)
    for i, data in ipairs(temp) do
        profile.bars[data.key][orderField] = i
    end
end

local function getCount(profile, blockField, block)
    local count = 0
    for _, bar in pairs(profile.bars) do
        if bar[blockField] == block then
            count = count + 1
        end
    end
    return count
end

-------------------------------------------------------------------------------
-- STYLE HELPER
-------------------------------------------------------------------------------

local function getStyle(elementID)
    return ascensionBars.db.profile.elementStyles and ascensionBars.db.profile.elementStyles[elementID] or {}
end

-------------------------------------------------------------------------------
-- UI FACTORY (refactored with options table)
-------------------------------------------------------------------------------

local function createHeader(args)
    local elementID = args.elementID
    local parent = args.parent
    local text = args.text
    local yOffset = args.yOffset
    ascensionBars.registeredElements = ascensionBars.registeredElements or {}
    ascensionBars.registeredElements[elementID] = "Header"
    local style = getStyle(elementID)
    local headerSize = style.uiHeaderSize or menuStyle.uiHeaderSize or 18
    local headerColor = style.uiHeaderColor or menuStyle.uiHeaderColor or colors.gold
    local hSpacing = style.headerSpacing or menuStyle.headerSpacing
    local dSpacing = style.dividerSpacing or menuStyle.dividerSpacing

    local header = parent:CreateFontString(nil, "OVERLAY", "GameFontNormalHuge")
    header.elementID = elementID
    header:SetPoint("TOPLEFT", style.contentPadding or menuStyle.contentPadding, yOffset)
    header:SetText(text)
    header:SetTextColor(unpack(headerColor))

    local divider = parent:CreateTexture(nil, "ARTWORK")
    divider:SetHeight(1)
    divider:SetPoint("TOPLEFT", header, "BOTTOMLEFT", 0, -dSpacing)
    divider:SetPoint("RIGHT", parent, "RIGHT", -5, 0)
    divider:SetColorTexture(unpack(colors.surface_highlight))

    return header, yOffset - hSpacing
end

local function createLabel(args)
    local elementID = args.elementID
    local parent = args.parent
    local text = args.text
    local yOffset = args.yOffset
    local xOffset = args.xOffset
    ascensionBars.registeredElements = ascensionBars.registeredElements or {}
    ascensionBars.registeredElements[elementID] = "Label"
    local style = getStyle(elementID)
    local textColor = style.text_light or colors.text_light
    local lSpacing = style.labelSpacing or menuStyle.labelSpacing
    local actualX = style.contentPadding or xOffset or menuStyle.contentPadding

    local label = parent:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
    label.elementID = elementID
    label:SetPoint("TOPLEFT", actualX, yOffset)
    label:SetText(text)
    label:SetTextColor(unpack(textColor))
    return label, yOffset - lSpacing
end

local function createCheckbox(args)
    local elementID = args.elementID
    local parent = args.parent
    local text = args.text
    local tooltip = args.tooltip
    local getter = args.getter
    local setter = args.setter
    local yOffset = args.yOffset
    local xOffset = args.xOffset
    ascensionBars.registeredElements = ascensionBars.registeredElements or {}
    ascensionBars.registeredElements[elementID] = "Checkbox"
    local style = getStyle(elementID)
    local actualParent = parent or menuStyle.contentPanel
    local size = style.checkboxSize or menuStyle.checkboxSize
    local spacing = style.checkboxSpacing or menuStyle.checkboxSpacing
    local labelColor = style.textColor or { 0.88, 0.88, 0.88, 1 }

    local checkbox = CreateFrame("CheckButton", nil, actualParent, "UICheckButtonTemplate")
    checkbox.elementID = elementID
    checkbox:SetSize(size, size)
    local actualX = style.contentPadding or xOffset or 10
    checkbox:SetPoint("TOPLEFT", actualParent, "TOPLEFT", actualX, yOffset or -10)

    local label = checkbox.Text
    label:SetFontObject("GameFontHighlight")
    label:SetPoint("LEFT", checkbox, "RIGHT", 5, 0)
    label:SetText(text or "")
    label:SetTextColor(unpack(labelColor))

    if getter then
        checkbox:SetChecked(getter())
    end

    checkbox:SetScript("OnClick", function(self)
        if setter then
            setter(self:GetChecked())
        end
    end)

    if tooltip then
        checkbox:SetScript("OnEnter", function(self)
            GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
            GameTooltip:SetText(text or "", 1, 1, 1)
            GameTooltip:AddLine(tooltip, 1, 0.82, 0, true)
            GameTooltip:Show()
        end)
        checkbox:SetScript("OnLeave", GameTooltip_Hide)
    end

    return checkbox, yOffset - (size + spacing)
end

local function createSlider(args)
    local elementID = args.elementID
    local parent = args.parent
    local text = args.text
    local minVal = args.minVal
    local maxVal = args.maxVal
    local step = args.step
    local getter = args.getter
    local setter = args.setter
    local width = args.width
    local yOffset = args.yOffset
    local xOffset = args.xOffset
    ascensionBars.registeredElements = ascensionBars.registeredElements or {}
    ascensionBars.registeredElements[elementID] = "Slider"
    local style = getStyle(elementID)
    local sliderSpacing = style.sliderSpacing or menuStyle.sliderSpacing
    local defaultWidth = style.sliderWidth or menuStyle.sliderWidth
    local sliderWidth = width or defaultWidth
    local actualX = style.contentPadding or xOffset or menuStyle.contentPadding

    local sliderName = "AscensionBarsSlider_" .. tostring(math.random(1000000, 9999999))
    local slider = CreateFrame("Slider", sliderName, parent, "OptionsSliderTemplate")
    slider.elementID = elementID
    slider:SetPoint("TOPLEFT", actualX, yOffset - 15)
    slider:SetWidth(sliderWidth)

    slider:SetMinMaxValues(minVal, maxVal)
    slider:SetValueStep(step)
    slider:SetObeyStepOnDrag(true)

    local val = getter() or minVal
    slider.text = slider:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
    slider.text:SetPoint("BOTTOM", slider, "TOP", 0, 3)
    slider.text:SetText(text)

    local low = _G[sliderName .. "Low"]
    local high = _G[sliderName .. "High"]
    if low then low:SetText(minVal) end
    if high then high:SetText(maxVal) end

    local editBox = CreateFrame("EditBox", nil, parent, "InputBoxTemplate")
    editBox:SetSize(50, 20)
    editBox:SetPoint("TOP", slider, "BOTTOM", 0, -4)
    editBox:SetAutoFocus(false)
    editBox:SetJustifyH("CENTER")
    editBox:SetText(tostring(math.floor(val * 100) / 100))

    local function updateValue(inputValue)
        local numericValue = tonumber(inputValue)
        if numericValue then
            if numericValue < minVal then numericValue = minVal end
            if numericValue > maxVal then numericValue = maxVal end
            slider:SetValue(numericValue)
            editBox:SetText(tostring(math.floor(numericValue * 100) / 100))
            setter(numericValue)
        end
    end

    editBox:SetScript("OnEnterPressed", function(self)
        updateValue(self:GetText())
        self:ClearFocus()
    end)

    local buttonMinus = CreateFrame("Button", nil, parent, "UIPanelButtonTemplate")
    buttonMinus:SetSize(20, 20)
    buttonMinus:SetText("-")
    buttonMinus:SetPoint("RIGHT", editBox, "LEFT", -5, 0)
    buttonMinus:SetScript("OnClick", function()
        updateValue(slider:GetValue() - step)
    end)

    local buttonPlus = CreateFrame("Button", nil, parent, "UIPanelButtonTemplate")
    buttonPlus:SetSize(20, 20)
    buttonPlus:SetText("+")
    buttonPlus:SetPoint("LEFT", editBox, "RIGHT", 5, 0)
    buttonPlus:SetScript("OnClick", function()
        updateValue(slider:GetValue() + step)
    end)

    slider:SetValue(val)
    slider:SetScript("OnValueChanged", function(_, value)
        setter(value)
        editBox:SetText(tostring(math.floor(value * 100) / 100))
    end)

    return slider, yOffset - sliderSpacing
end

local function createColorPicker(args)
    local elementID = args.elementID
    local parent = args.parent
    local text = args.text
    local getter = args.getter
    local setter = args.setter
    local yOffset = args.yOffset
    local xOffset = args.xOffset
    local hasAlpha = args.hasAlpha
    ascensionBars.registeredElements = ascensionBars.registeredElements or {}
    ascensionBars.registeredElements[elementID] = "ColorPicker"
    local style = getStyle(elementID)
    local actualX = style.contentPadding or xOffset or menuStyle.contentPadding
    local pickerSize = style.colorPickerSize or menuStyle.colorPickerSize or 20
    local pickerSpacing = style.colorPickerSpacing or menuStyle.colorPickerSpacing
    local button = CreateFrame("Button", nil, parent)

    button.elementID = elementID
    button:SetSize(pickerSize, pickerSize)
    button:SetPoint("TOPLEFT", actualX, yOffset)

    local texture = button:CreateTexture(nil, "OVERLAY")
    texture:SetAllPoints()
    texture:SetColorTexture(getter())
    button.tex = texture

    local background = button:CreateTexture(nil, "BACKGROUND")
    background:SetPoint("TOPLEFT", -1, 1)
    background:SetPoint("BOTTOMRIGHT", 1, -1)
    background:SetColorTexture(1, 1, 1, 1)

    local label = button:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
    label:SetPoint("LEFT", button, "RIGHT", 10, 0)
    label:SetText(text)

    local function colorCallback(restore)
        local r, g, b, a
        if restore then
            r, g, b, a = unpack(restore)
        else
            if ColorPickerFrame.GetColorRGB then
                r, g, b = ColorPickerFrame:GetColorRGB()
            end
            a = (ColorPickerFrame.GetColorAlpha and ColorPickerFrame:GetColorAlpha()) or
                (ColorPickerFrame.GetColorOpacity and 1 - ColorPickerFrame:GetColorOpacity()) or 1
        end
        local finalAlpha = a or 1
        setter(r or 1, g or 1, b or 1, finalAlpha)
        button.tex:SetColorTexture(r or 1, g or 1, b or 1, finalAlpha)
    end

    button:SetScript("OnClick", function()
        local r, g, b, a = getter()
        local currentAlpha = a or 1
        local info = {
            swatchFunc = function() colorCallback() end,
            opacityFunc = function() colorCallback() end,
            cancelFunc = function() colorCallback({ r or 1, g or 1, b or 1, currentAlpha }) end,
            hasOpacity = hasAlpha,
            opacity = currentAlpha,
            r = r or 1,
            g = g or 1,
            b = b or 1
        }

        if ColorPickerFrame.SetupColorPickerAndShow then
            ColorPickerFrame:SetupColorPickerAndShow(info)
        else
            ColorPickerFrame.func = info.swatchFunc
            ColorPickerFrame.opacityFunc = info.opacityFunc
            ColorPickerFrame.cancelFunc = info.cancelFunc
            ColorPickerFrame.hasOpacity = info.hasOpacity
            ColorPickerFrame.opacity = info.opacity
            if ColorPickerFrame.SetColorRGB then ColorPickerFrame:SetColorRGB(info.r, info.g, info.b) end
            ColorPickerFrame:Show()
        end
    end)
    return button, yOffset - pickerSpacing
end

local function createDropdown(args)
    local elementID = args.elementID
    local parent = args.parent
    local text = args.text
    local options = args.options
    local getter = args.getter
    local setter = args.setter
    local yOffset = args.yOffset
    local xOffset = args.xOffset
    ascensionBars.registeredElements = ascensionBars.registeredElements or {}
    ascensionBars.registeredElements[elementID] = "Dropdown"
    local style = getStyle(elementID)
    local actualX = style.contentPadding or xOffset or menuStyle.contentPadding
    local dropWidth = style.dropdownWidth or menuStyle.dropdownWidth
    local dropHeight = style.dropdownHeight or menuStyle.dropdownHeight

    local frame = CreateFrame("Frame", nil, parent)
    frame.elementID = elementID
    frame:SetSize(dropWidth, dropHeight)
    frame:SetPoint("TOPLEFT", actualX, yOffset)

    local label = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
    label:SetPoint("TOPLEFT", 0, 0)
    label:SetText(text)

    local dropdown = CreateFrame("Button", nil, frame, "BackdropTemplate")
    dropdown:SetSize(dropWidth, 24)
    dropdown:SetPoint("BOTTOMLEFT", 0, 0)
    dropdown:SetBackdrop({
        bgFile = files.bgfile,
        edgeFile = files.edgefile,
        edgeSize = 10,
        insets = { left = 2, right = 2, top = 2, bottom = 2 }
    })
    dropdown:SetBackdropColor(unpack(colors.surface_highlight))
    dropdown:SetBackdropBorderColor(unpack(colors.black_detail))

    local dropdownText = dropdown:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
    dropdownText:SetPoint("LEFT", 10, 0)
    dropdownText:SetPoint("RIGHT", -20, 0)
    dropdownText:SetJustifyH("LEFT")

    local function getLabel(val)
        for _, opt in ipairs(options) do
            if opt.value == val then return opt.label end
        end
        return tostring(val)
    end
    dropdownText:SetText(getLabel(getter()))

    local arrow = dropdown:CreateTexture(nil, "OVERLAY")
    arrow:SetSize(20, 20)
    arrow:SetPoint("RIGHT", -5, 0)
    arrow:SetTexture(files.arrow)
    arrow:SetDesaturated(true)

    local list = CreateFrame("Frame", nil, dropdown, "BackdropTemplate")
    list:SetPoint("TOPLEFT", dropdown, "BOTTOMLEFT", 0, -2)
    list:SetWidth(150)
    list:SetFrameStrata("DIALOG")
    list:Hide()
    list:SetBackdrop({
        bgFile = files.bgfile,
        edgeFile = files.edgefile,
        edgeSize = 12,
        insets = { left = 2, right = 2, top = 2, bottom = 2 }
    })
    list:SetBackdropColor(unpack(colors.surface_dark))
    list:SetBackdropBorderColor(unpack(colors.surface_highlight))

    dropdown:SetScript("OnClick", function()
        if list:IsShown() then
            list:Hide()
        else
            list:Show()
        end
    end)

    list:SetHeight(#options * 20 + 10)

    for i, opt in ipairs(options) do
        local button = CreateFrame("Button", nil, list, "BackdropTemplate")
        button:SetSize(140, 20)
        button:SetPoint("TOPLEFT", 5, -5 - ((i - 1) * 20))
        button:SetBackdrop({ bgFile = files.bgfile })
        button:SetBackdropColor(unpack(colors.surface_dark))

        local buttonTextLocal = button:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
        buttonTextLocal:SetPoint("LEFT", 5, 0)
        buttonTextLocal:SetText(opt.label)

        button:SetScript("OnEnter", function(self)
            self:SetBackdropColor(unpack(colors.surface_highlight))
        end)
        button:SetScript("OnLeave", function(self)
            self:SetBackdropColor(unpack(colors.surface_dark))
        end)
        button:SetScript("OnClick", function()
            setter(opt.value)
            dropdownText:SetText(opt.label)
            list:Hide()
        end)
    end

    return frame, yOffset - 55
end

local function createScrollPanel(args)
    local elementID = args.elementID
    local parent = args.parent
    ascensionBars.registeredElements = ascensionBars.registeredElements or {}
    ascensionBars.registeredElements[elementID] = "ScrollPanel"
    local scrollName = "AscensionBarsScrollPanel_" .. tostring(math.random(1000000, 9999999))
    local scrollFrame = CreateFrame("ScrollFrame", scrollName, parent, "UIPanelScrollFrameTemplate")
    scrollFrame.elementID = elementID
    scrollFrame:SetPoint("TOPLEFT", 5, -5)
    scrollFrame:SetPoint("BOTTOMRIGHT", -25, 5)

    local content = CreateFrame("Frame", nil, scrollFrame)
    content:SetSize(scrollFrame:GetWidth(), 800)
    scrollFrame:SetScrollChild(content)
    scrollFrame:EnableMouseWheel(true)
    scrollFrame.ScrollBar = _G[scrollName .. "ScrollBar"]

    scrollFrame:SetScript("OnMouseWheel", function(self, delta)
        local scrollBar = self.ScrollBar
        if not scrollBar then return end
        local minVal, maxVal = scrollBar:GetMinMaxValues()
        local currentVal = scrollBar:GetValue()
        local newVal = currentVal - delta * 50
        if newVal < minVal then newVal = minVal end
        if newVal > maxVal then newVal = maxVal end
        scrollBar:SetValue(newVal)
    end)

    return scrollFrame, content
end

-------------------------------------------------------------------------------
-- LAYOUT HELPER
-------------------------------------------------------------------------------

local layoutModel = {}
function layoutModel:new(parent, startY)
    local obj = { parent = parent, y = startY or -15 }
    setmetatable(obj, { __index = layoutModel })
    return obj
end

function layoutModel:header(elementID, text)
    local h, newY = createHeader { elementID = elementID, parent = self.parent, text = text, yOffset = self.y }
    self.y = newY
    return h
end

function layoutModel:label(elementID, text, xOffset)
    local l, newY = createLabel { elementID = elementID, parent = self.parent, text = text, yOffset = self.y, xOffset = xOffset }
    self.y = newY
    return l
end

function layoutModel:checkbox(elementID, text, tooltip, getter, setter, xOffset)
    local cb, newY = createCheckbox {
        elementID = elementID, parent = self.parent, text = text, tooltip = tooltip,
        getter = getter, setter = setter, yOffset = self.y, xOffset = xOffset
    }
    self.y = newY
    return cb
end

function layoutModel:slider(elementID, text, minVal, maxVal, step, getter, setter, width, xOffset)
    local s, newY = createSlider {
        elementID = elementID, parent = self.parent, text = text, minVal = minVal, maxVal = maxVal, step = step,
        getter = getter, setter = setter, width = width, yOffset = self.y, xOffset = xOffset
    }
    self.y = newY
    return s
end

function layoutModel:colorPicker(elementID, text, getter, setter, xOffset, hasAlpha)
    local cp, newY = createColorPicker {
        elementID = elementID, parent = self.parent, text = text, getter = getter, setter = setter,
        yOffset = self.y, xOffset = xOffset, hasAlpha = hasAlpha
    }
    self.y = newY
    return cp
end

function layoutModel:dropdown(elementID, text, options, getter, setter, xOffset)
    local dd, newY = createDropdown {
        elementID = elementID, parent = self.parent, text = text, options = options,
        getter = getter, setter = setter, yOffset = self.y, xOffset = xOffset
    }
    self.y = newY
    return dd
end

-------------------------------------------------------------------------------
-- BAR & TEXT CONTROL BUILDERS
-------------------------------------------------------------------------------

local function createBarControls(layout, profile, barKey, displayName, anchorBlock, controlWidth, xOffset)
    local bar = profile.bars[barKey]

    layout:label("BarHeader_" .. barKey, displayName, xOffset + 5)
    layout:checkbox("EnableBarCheckbox_" .. barKey, Locales["ENABLE"], nil,
        function() return bar.enabled end,
        function(v)
            bar.enabled = v; ascensionBars:updateDisplay()
        end,
        xOffset + 10)

    layout:dropdown("AnchorDropdown_" .. barKey, Locales["ANCHOR"],
        {
            { label = Locales["TOP"],    value = "TOP" },
            { label = Locales["BOTTOM"], value = "BOTTOM" },
            { label = Locales["FREE"],   value = "FREE" }
        },
        function() return bar.block end,
        function(v)
            local oldBlock = bar.block
            bar.block = v
            cleanOrders(profile, "block", "order", oldBlock)
            bar.order = math.max(1, getCount(profile, "block", v))
            ascensionBars:updateDisplay()
        end,
        xOffset + 10)

    local orderOptions = {}
    local count = getCount(profile, "block", bar.block)
    for j = 1, count do
        table.insert(orderOptions, { label = tostring(j), value = j })
    end
    layout:dropdown("OrderDropdown_" .. barKey, Locales["ORDER"], orderOptions,
        function() return bar.order or 1 end,
        function(v)
            local oldOrder = bar.order or 1
            if oldOrder == v then return end
            for k, b in pairs(profile.bars) do
                if k ~= barKey and b.block == bar.block then
                    local barOrder = b.order or 1
                    if oldOrder < v then
                        if barOrder > oldOrder and barOrder <= v then
                            b.order = barOrder - 1
                        end
                    else
                        if barOrder >= v and barOrder < oldOrder then
                            b.order = barOrder + 1
                        end
                    end
                end
            end
            bar.order = v
            ascensionBars:updateDisplay()
        end,
        xOffset + 10)

    if profile.textLayoutMode == "INDIVIDUAL_LINES" then
        layout:slider("TextXSlider_" .. barKey, Locales["TXT_X"], -500, 500, 1,
            function() return bar.textX or 0 end,
            function(v)
                bar.textX = v; ascensionBars:updateDisplay()
            end,
            controlWidth, xOffset + 15)

        layout:slider("TextYSlider_" .. barKey, Locales["TXT_Y"], -500, 500, 1,
            function() return bar.textY or 0 end,
            function(v)
                bar.textY = v; ascensionBars:updateDisplay()
            end,
            controlWidth, xOffset + 15)
    end

    if bar.block == "FREE" then
        layout:slider("WidthSlider_" .. barKey, Locales["WIDTH"], 50, 2000, 1,
            function() return bar.freeWidth end,
            function(v)
                bar.freeWidth = v; ascensionBars:updateDisplay()
            end,
            controlWidth, xOffset + 15)

        layout:slider("HeightSlider_" .. barKey, Locales["HEIGHT"], 2, 100, 1,
            function() return bar.freeHeight or 15 end,
            function(v)
                bar.freeHeight = v; ascensionBars:updateDisplay()
            end,
            controlWidth, xOffset + 15)

        layout:slider("PosXSlider_" .. barKey, Locales["POS_X"], -1000, 1000, 1,
            function() return bar.freeX end,
            function(v)
                bar.freeX = v; ascensionBars:updateDisplay()
            end,
            controlWidth, xOffset + 15)

        layout:slider("PosYSlider_" .. barKey, Locales["POS_Y"], -1000, 1000, 1,
            function() return bar.freeY end,
            function(v)
                bar.freeY = v; ascensionBars:updateDisplay()
            end,
            controlWidth, xOffset + 15)
    end

    layout.y = layout.y - 15
end

local function createTextControls(layout, profile, barKey, anchorBlock, xOffset)
    local bar = profile.bars[barKey]

    layout:label("TextHeader_" .. barKey, barKey, xOffset + 5)
    layout:dropdown("TextAnchorDropdown_" .. barKey, Locales["ANCHOR"],
        {
            { label = Locales["GROUP_1"], value = "T1" },
            { label = Locales["GROUP_2"], value = "T2" },
            { label = Locales["GROUP_3"], value = "T3" }
        },
        function() return bar.textBlock or "T1" end,
        function(v)
            local oldBlock = bar.textBlock or "T1"
            bar.textBlock = v
            cleanOrders(profile, "textBlock", "textOrder", oldBlock)
            bar.textOrder = math.max(1, getCount(profile, "textBlock", v))
            ascensionBars:updateDisplay()
        end,
        xOffset + 10)

    local orderOptions = {}
    local count = getCount(profile, "textBlock", bar.textBlock or "T1")
    for j = 1, count do
        table.insert(orderOptions, { label = tostring(j), value = j })
    end
    layout:dropdown("TextOrderDropdown_" .. barKey, Locales["ORDER"], orderOptions,
        function() return bar.textOrder or 1 end,
        function(v)
            local oldOrder = bar.textOrder or 1
            if oldOrder == v then return end
            for k, b in pairs(profile.bars) do
                if k ~= barKey and (b.textBlock or "T1") == (bar.textBlock or "T1") then
                    local barOrder = b.textOrder or 1
                    if oldOrder < v then
                        if barOrder > oldOrder and barOrder <= v then
                            b.textOrder = barOrder - 1
                        end
                    else
                        if barOrder >= v and barOrder < oldOrder then
                            b.textOrder = barOrder + 1
                        end
                    end
                end
            end
            bar.textOrder = v
            ascensionBars:updateDisplay()
        end,
        xOffset + 10)

    layout.y = layout.y - 15
end

-------------------------------------------------------------------------------
-- CONTENT CLEANUP
-------------------------------------------------------------------------------

local function cleanupContent(contentPanel)
    if not contentPanel then return end
    for _, child in ipairs({ contentPanel:GetChildren() }) do
        child:Hide()
        child:ClearAllPoints()
    end
    for _, region in ipairs({ contentPanel:GetRegions() }) do
        if region.Hide then region:Hide() end
    end
end

-------------------------------------------------------------------------------
-- TAB BUILD FUNCTIONS
-------------------------------------------------------------------------------

local function buildLayoutTab(panel)
    cleanupContent(panel.content)
    local content = panel.content
    local profile = ascensionBars.db.profile
    if not profile then return end

    local scrollFrame = panel.scrollFrame
    local panelWidth = scrollFrame and scrollFrame:GetWidth() - 30 or 600
    panelWidth = math.max(panelWidth, 400)
    local colWidth = (panelWidth - 80) / 3

    -- Global sliders (manual horizontal placement)
    local y = -15
    local _, newY1 = createSlider {
        elementID = "GlobalBlocksOffsetSlider", parent = content,
        text = Locales["GLOBAL_BLOCKS_OFFSET"], minVal = -100, maxVal = 100, step = 1,
        getter = function() return profile.yOffset end,
        setter = function(v)
            profile.yOffset = v; ascensionBars:updateDisplay()
        end,
        width = 180, yOffset = y, xOffset = 15
    }
    local _, newY2 = createSlider {
        elementID = "GlobalBarHeightSlider", parent = content,
        text = Locales["GLOBAL_BAR_HEIGHT"], minVal = 1, maxVal = 50, step = 1,
        getter = function() return profile.globalBarHeight end,
        setter = function(v)
            profile.globalBarHeight = v; for _, b in pairs(profile.bars) do b.freeHeight = v end; ascensionBars
                :updateDisplay()
        end,
        width = 180, yOffset = y, xOffset = 210
    }
    local mainY = math.min(newY1, newY2) - 10

    local mainLayout = layoutModel:new(content, mainY)
    mainLayout:header("BarManagementHeader", Locales["BAR_MANAGEMENT"])
    local startY = mainLayout.y

    local blocks = {
        { name = Locales["TOP_BLOCK"],    key = "TOP",    x = 10 },
        { name = Locales["BOTTOM_BLOCK"], key = "BOTTOM", x = 10 + colWidth + 5 },
        { name = Locales["FREE_MODE"],    key = "FREE",   x = 10 + (colWidth + 5) * 2 }
    }

    local barKeys = { "XP", "Rep", "Honor", "HouseXp", "Azerite" }
    local barNames = { Locales["EXPERIENCE"], Locales["REPUTATION"], Locales["HONOR"], Locales["HOUSE_FAVOR"], Locales
        ["AZERITE"] }

    local maxBottomY = startY

    for _, block in ipairs(blocks) do
        local layout = layoutModel:new(content, startY)
        local header = content:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
        header:SetPoint("TOPLEFT", block.x, startY)
        header:SetText(block.name)
        header:SetTextColor(unpack(colors.primary))
        layout.y = startY - 25

        local sorted = {}
        for i, key in ipairs(barKeys) do
            if profile.bars[key].block == block.key then
                table.insert(sorted, { key = key, name = barNames[i] })
            end
        end
        table.sort(sorted, function(a, b) return profile.bars[a.key].order < profile.bars[b.key].order end)

        for _, bar in ipairs(sorted) do
            createBarControls(layout, profile, bar.key, bar.name, block.key, colWidth - 40, block.x)
        end

        if #sorted == 0 then
            local empty = content:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
            empty:SetPoint("TOPLEFT", block.x + 10, layout.y)
            empty:SetText(Locales["EMPTY"])
            empty:SetTextColor(0.5, 0.5, 0.5, 1)
            layout.y = layout.y - 20
        end

        if layout.y < maxBottomY then maxBottomY = layout.y end
    end

    content:SetHeight(math.abs(maxBottomY) + 50)
end

local function buildAppearanceTab(panel)
    cleanupContent(panel.content)
    local content = panel.content
    local profile = ascensionBars.db.profile
    if not profile then return end

    local layout = layoutModel:new(content, -15)
    layout:header("TextAndFontHeader", Locales["TEXT_AND_FONT"])
    layout:dropdown("LayoutModeDropdown", Locales["LAYOUT_MODE"],
        {
            { label = Locales["ALL_IN_ONE_LINE"], value = "SINGLE_LINE" },
            { label = Locales["MULTIPLE_LINES"],  value = "INDIVIDUAL_LINES" }
        },
        function() return profile.textLayoutMode end,
        function(v)
            profile.textLayoutMode = v; ascensionBars:updateDisplay(); panel:UpdateLayout()
        end,
        15)

    if profile.textLayoutMode == "INDIVIDUAL_LINES" then
        layout:checkbox("TextFollowsBarCheckbox", Locales["TEXT_FOLLOWS_BAR"], Locales["TEXT_FOLLOWS_BAR_DESC"],
            function() return profile.textFollowBar end,
            function(v)
                profile.textFollowBar = v; ascensionBars:updateDisplay()
            end,
            25)
    end

    layout:slider("FontSizeSlider", Locales["FONT_SIZE"], 8, 32, 1,
        function() return profile.textSize end,
        function(v)
            profile.textSize = v; ascensionBars:updateDisplay()
        end,
        180, 15)

    layout:colorPicker("GlobalTextColorPicker", Locales["GLOBAL_TEXT_COLOR"],
        function()
            local c = profile.textColor; return c.r, c.g, c.b, c.a
        end,
        function(r, g, b, a)
            profile.textColor.r = r; profile.textColor.g = g; profile.textColor.b = b; profile.textColor.a = a; ascensionBars
                :updateDisplay()
        end,
        15, true)

    layout:header("TextGroupPositionsHeader", Locales["TEXT_GROUP_POSITIONS"])
    local startY = layout.y
    local panelWidth = panel.scrollFrame and panel.scrollFrame:GetWidth() - 30 or 600
    panelWidth = math.max(panelWidth, 400)
    local colWidth = (panelWidth - 30) / 3
    local maxColY = startY

    for i = 1, 3 do
        local groupKey = "T" .. i
        local xOff = 15 + (i - 1) * colWidth
        local layoutGroup = layoutModel:new(content, startY)
        local settings = profile.textGroups[groupKey] or { detached = true, x = 0, y = -25 * i }

        layoutGroup:checkbox("DetachGroupCheckbox_" .. i, string.format(Locales["DETACH_GROUP"], i),
            string.format(Locales["DETACH_GROUP_DESC"], i),
            function() return settings.detached end,
            function(v)
                settings.detached = v; ascensionBars:updateDisplay(); panel:UpdateLayout()
            end,
            xOff)

        if settings.detached then
            layoutGroup:slider("GroupXSlider_" .. i, string.format(Locales["GROUP_X"], i), -1000, 1000, 1,
                function() return settings.x end,
                function(v)
                    settings.x = v; ascensionBars:updateDisplay()
                end,
                colWidth - 20, xOff + 5)
            layoutGroup:slider("GroupYSlider_" .. i, string.format(Locales["GROUP_Y"], i), -1000, 1000, 1,
                function() return settings.y end,
                function(v)
                    settings.y = v; ascensionBars:updateDisplay()
                end,
                colWidth - 20, xOff + 5)
        end

        if layoutGroup.y < maxColY then maxColY = layoutGroup.y end
    end

    layout.y = maxColY - 10
    layout:header("TextManagementHeader", Locales["TEXT_MANAGEMENT"])
    startY = layout.y

    local textBlocks = {
        { name = Locales["GROUP_1"], key = "T1", x = 10 },
        { name = Locales["GROUP_2"], key = "T2", x = 10 + colWidth + 5 },
        { name = Locales["GROUP_3"], key = "T3", x = 10 + (colWidth + 5) * 2 }
    }
    local maxBottomY = startY

    for _, block in ipairs(textBlocks) do
        local layout = layoutModel:new(content, startY)
        local header = content:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
        header:SetPoint("TOPLEFT", block.x, startY)
        header:SetText(block.name)
        header:SetTextColor(unpack(colors.primary))
        layout.y = startY - 25

        local sorted = {}
        for k, b in pairs(profile.bars) do
            if b.textBlock == block.key then
                table.insert(sorted, { key = k, order = b.textOrder or 0 })
            end
        end
        table.sort(sorted, function(a, b) return a.order < b.order end)

        for _, td in ipairs(sorted) do
            createTextControls(layout, profile, td.key, block.key, block.x)
        end

        if #sorted == 0 then
            local empty = content:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
            empty:SetPoint("TOPLEFT", block.x + 10, layout.y)
            empty:SetText(Locales["EMPTY"])
            empty:SetTextColor(0.5, 0.5, 0.5, 1)
            layout.y = layout.y - 20
        end

        if layout.y < maxBottomY then maxBottomY = layout.y end
    end

    content:SetHeight(math.abs(maxBottomY) + 20)
end

local function buildBehaviorTab(panel)
    cleanupContent(panel.content)
    local content = panel.content
    local profile = ascensionBars.db.profile
    if not profile then return end

    local layout = layoutModel:new(content, -15)
    layout:header("AutoHideLogicHeader", Locales["AUTO_HIDE_LOGIC"])
    layout:checkbox("ShowOnMouseoverCheckbox", Locales["SHOW_ON_MOUSEOVER"], nil,
        function() return profile.showOnMouseover end,
        function(v)
            profile.showOnMouseover = v; ascensionBars:updateDisplay()
        end)
    layout:checkbox("HideInCombatCheckbox", Locales["HIDE_IN_COMBAT"], nil,
        function() return profile.hideInCombat end,
        function(v)
            profile.hideInCombat = v; ascensionBars:updateDisplay()
        end)
    layout:checkbox("HideAtMaxLevelCheckbox", Locales["HIDE_AT_MAX_LEVEL"], nil,
        function() return profile.hideAtMaxLevel end,
        function(v)
            profile.hideAtMaxLevel = v; ascensionBars:updateDisplay()
        end)

    layout:header("DataDisplayHeader", Locales["DATA_DISPLAY"])
    layout:checkbox("ShowPercentageCheckbox", Locales["SHOW_PERCENTAGE"], nil,
        function() return profile.showPercentage end,
        function(v)
            profile.showPercentage = v; ascensionBars:updateDisplay()
        end)
    layout:checkbox("ShowAbsoluteValuesCheckbox", Locales["SHOW_ABSOLUTE_VALUES"], nil,
        function() return profile.showAbsoluteValues end,
        function(v)
            profile.showAbsoluteValues = v; ascensionBars:updateDisplay()
        end)
    layout:checkbox("ShowSparkCheckbox", Locales["SHOW_SPARK"], nil,
        function() return profile.sparkEnabled end,
        function(v)
            profile.sparkEnabled = v; ascensionBars:updateDisplay()
        end)

    content:SetHeight(math.abs(layout.y) + 20)
end

local function buildColorsTab(panel)
    cleanupContent(panel.content)
    local content = panel.content
    local profile = ascensionBars.db.profile
    if not profile then return end

    local layout = layoutModel:new(content, -15)

    -- Experience
    layout:header("ExperienceHeader", Locales["EXPERIENCE"])
    layout:checkbox("UseClassColorXPCheckbox", Locales["USE_CLASS_COLOR"], nil,
        function() return profile.useClassColorXP end,
        function(v)
            profile.useClassColorXP = v; ascensionBars:updateDisplay(); panel:UpdateLayout()
        end)
    if not profile.useClassColorXP then
        layout:colorPicker("CustomXPColorPicker", Locales["CUSTOM_XP_COLOR"],
            function()
                local c = profile.xpBarColor; return c.r, c.g, c.b, c.a
            end,
            function(r, g, b, a)
                profile.xpBarColor.r = r; profile.xpBarColor.g = g; profile.xpBarColor.b = b; profile.xpBarColor.a = a; ascensionBars
                    :updateDisplay()
            end,
            25, true)
    end
    layout:checkbox("ShowRestedBarCheckbox", Locales["SHOW_RESTED_BAR"], nil,
        function() return profile.showRestedBar end,
        function(v)
            profile.showRestedBar = v; ascensionBars:updateDisplay(); panel:UpdateLayout()
        end)
    if profile.showRestedBar then
        layout:colorPicker("RestedColorPicker", Locales["RESTED_COLOR"],
            function()
                local c = profile.restedBarColor; return c.r, c.g, c.b, c.a
            end,
            function(r, g, b, a)
                profile.restedBarColor.r = r; profile.restedBarColor.g = g; profile.restedBarColor.b = b; profile.restedBarColor.a =
                    a; ascensionBars:updateDisplay()
            end,
            25, true)
    end

    -- Reputation
    layout:header("ReputationHeader", Locales["REPUTATION"])
    layout:checkbox("UseReactionColorsCheckbox", Locales["USE_REACTION_COLORS"], nil,
        function() return profile.useReactionColorRep end,
        function(v)
            profile.useReactionColorRep = v; ascensionBars:updateDisplay(); panel:UpdateLayout()
        end)
    if not profile.useReactionColorRep then
        layout:colorPicker("CustomRepColorPicker", Locales["CUSTOM_REP_COLOR"],
            function()
                local c = profile.repBarColor; return c.r, c.g, c.b, c.a
            end,
            function(r, g, b, a)
                profile.repBarColor.r = r; profile.repBarColor.g = g; profile.repBarColor.b = b; profile.repBarColor.a =
                    a; ascensionBars:updateDisplay()
            end,
            25, true)
    else
        local standingLabels = {
            Locales["HATED"], Locales["HOSTILE"], Locales["UNFRIENDLY"], Locales["NEUTRAL"],
            Locales["FRIENDLY"], Locales["HONORED"], Locales["REVERED"], Locales["EXALTED"],
            Locales["PARAGON"], Locales["MAXED"], Locales["RENOWN"]
        }
        local startY = layout.y
        local colW = 190
        local lowestY = startY
        for i = 1, 11 do
            local colIdx = (i - 1) % 3
            local row = math.floor((i - 1) / 3)
            local xOff = 15 + colIdx * colW
            local yPos = startY - row * 35
            local _, newY = createColorPicker {
                elementID = "RepStandingColorPicker_" .. i,
                parent = content,
                text = standingLabels[i] or string.format(Locales["RANK_NUM"], i),
                getter = function()
                    local c = profile.repColors[i]; if not c then return 1, 1, 1, 1 end; return c.r, c.g, c.b, c.a
                end,
                setter = function(r, g, b, a)
                    if not profile.repColors[i] then profile.repColors[i] = {} end; local c = profile.repColors[i]; c.r =
                        r; c.g = g; c.b = b; c.a = a; ascensionBars:updateDisplay()
                end,
                yOffset = yPos,
                xOffset = xOff + 10,
                hasAlpha = true
            }
            if newY < lowestY then lowestY = newY end
        end
        layout.y = lowestY - 10
    end

    -- Honor
    layout:header("HonorHeader", Locales["HONOR"])
    layout:colorPicker("HonorColorPicker", Locales["HONOR_COLOR"],
        function()
            local c = profile.honorColor; if not c then return 0.8, 0.2, 0.2, 1 end; return c.r, c.g, c.b, c.a
        end,
        function(r, g, b, a)
            if not profile.honorColor then profile.honorColor = {} end; local c = profile.honorColor; c.r = r; c.g = g; c.b =
                b; c.a = a; ascensionBars:updateDisplay()
        end,
        15, true)

    -- House Favor
    layout:header("HouseFavorHeader", Locales["HOUSE_FAVOR"])
    layout:colorPicker("HouseXPColorPicker", Locales["HOUSE_XP_COLOR"],
        function()
            local c = profile.houseXpColor; if not c then return 0.9, 0.5, 0, 1 end; return c.r, c.g, c.b, c.a
        end,
        function(r, g, b, a)
            if not profile.houseXpColor then profile.houseXpColor = {} end; local c = profile.houseXpColor; c.r = r; c.g =
                g; c.b = b; c.a = a; ascensionBars:updateDisplay()
        end,
        15, true)
    layout:colorPicker("HouseRewardColorPicker", Locales["HOUSE_REWARD_COLOR"],
        function()
            local c = profile.houseRewardTextColor; if not c then return 0.9, 0.5, 0, 1 end; return c.r, c.g, c.b, c.a
        end,
        function(r, g, b, a)
            if not profile.houseRewardTextColor then profile.houseRewardTextColor = {} end; local c = profile
                .houseRewardTextColor; c.r = r; c.g = g; c.b = b; c.a = a; ascensionBars:updateDisplay()
        end,
        15, true)
    layout:slider("HouseRewardYOffsetSlider", Locales["HOUSE_REWARD_Y_OFFSET"], -1000, 500, 5,
        function() return profile.houseRewardTextYOffset or -40 end,
        function(v)
            profile.houseRewardTextYOffset = v; ascensionBars:updateDisplay()
        end,
        180, 15)

    -- Azerite
    layout:header("AzeriteHeader", Locales["AZERITE"])
    layout:colorPicker("AzeriteColorPicker", Locales["AZERITE_COLOR"],
        function()
            local c = profile.azeriteColor; if not c then return 0.9, 0.8, 0.5, 1 end; return c.r, c.g, c.b, c.a
        end,
        function(r, g, b, a)
            if not profile.azeriteColor then profile.azeriteColor = {} end; local c = profile.azeriteColor; c.r = r; c.g =
                g; c.b = b; c.a = a; ascensionBars:updateDisplay()
        end,
        15, true)

    content:SetHeight(math.abs(layout.y) + 20)
end

local function buildParagonTab(panel)
    cleanupContent(panel.content)
    local content = panel.content
    local profile = ascensionBars.db.profile
    if not profile then return end

    local layout = layoutModel:new(content, -15)
    layout:header("AlertStylingHeader", Locales["ALERT_STYLING"])
    layout:checkbox("ShowOnTopCheckbox", Locales["SHOW_ON_TOP"], nil,
        function() return profile.paragonOnTop end,
        function(v)
            profile.paragonOnTop = v; ascensionBars:updateDisplay()
        end)
    layout:checkbox("SplitLinesCheckbox", Locales["SPLIT_LINES"], nil,
        function() return profile.splitParagonText end,
        function(v)
            profile.splitParagonText = v; ascensionBars:updateDisplay()
        end)
    layout:slider("ParagonTextSizeSlider", Locales["TEXT_SIZE"], 10, 40, 1,
        function() return profile.paragonTextSize or 18 end,
        function(v)
            profile.paragonTextSize = v; ascensionBars:updateDisplay()
        end,
        180, 15)
    layout:slider("ParagonVerticalOffsetYSlider", Locales["VERTICAL_OFFSET_Y"], -1000, 500, 5,
        function() return profile.paragonTextYOffset or -100 end,
        function(v)
            profile.paragonTextYOffset = v; ascensionBars:updateDisplay()
        end,
        180, 15)
    layout:colorPicker("AlertColorPicker", Locales["ALERT_COLOR"],
        function()
            local c = profile.paragonPendingColor; return c.r, c.g, c.b, 1
        end,
        function(r, g, b)
            local c = profile.paragonPendingColor; c.r = r; c.g = g; c.b = b; ascensionBars:updateDisplay()
        end,
        15, false)

    content:SetHeight(math.abs(layout.y) + 20)
end

-------------------------------------------------------------------------------
-- TABBED INTERFACE FACTORY
-------------------------------------------------------------------------------

local function createTabbedInterface(parent, tabNames, buildFuncs, initialIndex)
    local tabs = {}
    local panels = {}
    local activeTab = initialIndex or 1

    local sidebarSeparator = parent:CreateTexture(nil, "ARTWORK")
    sidebarSeparator:SetWidth(1)
    sidebarSeparator:SetPoint("TOPLEFT", menuStyle.sidebarWidth, -45)
    sidebarSeparator:SetPoint("BOTTOMLEFT", menuStyle.sidebarWidth, 75)
    sidebarSeparator:SetColorTexture(unpack(colors.surface_highlight))

    local function selectTab(index)
        activeTab = index
        ascensionBars.activeTab = index
        for i, tab in ipairs(tabs) do
            if i == index then
                tab:SetBackdropColor(unpack(colors.sidebar_active))
            else
                tab:SetBackdropColor(0, 0, 0, 0)
            end
        end
        for i, panel in ipairs(panels) do
            if i == index then
                panel:Show()
                C_Timer.After(0.01, function()
                    if panel.UpdateLayout then panel.UpdateLayout() end
                end)
            else
                panel:Hide()
            end
        end
    end

    local function createTabButton(label, idx)
        local btn = CreateFrame("Button", nil, parent, "BackdropTemplate")
        btn:SetSize(140, 30)
        btn:SetPoint("TOPLEFT", 10, -50 - ((idx - 1) * 35))
        btn:SetBackdrop {
            bgFile = files.bgfile,
            edgeFile = files.edgefile,
            edgeSize = 1,
            insets = { left = 1, right = 1, top = 1, bottom = 1 }
        }
        btn:SetBackdropColor(0, 0, 0, 0)
        btn:SetBackdropBorderColor(0, 0, 0, 0)

        local text = btn:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
        text:SetPoint("LEFT", 15, 0)
        text:SetText(label)

        btn:SetScript("OnClick", function() selectTab(idx) end)
        btn:SetScript("OnEnter", function()
            if activeTab ~= idx then btn:SetBackdropColor(unpack(colors.sidebar_hover)) end
        end)
        btn:SetScript("OnLeave", function()
            if activeTab ~= idx then btn:SetBackdropColor(0, 0, 0, 0) end
        end)

        table.insert(tabs, btn)
        return btn
    end

    for i, name in ipairs(tabNames) do
        createTabButton(name, i)
        local panel = CreateFrame("Frame", nil, parent)
        panel:SetPoint("TOPLEFT", sidebarSeparator, "TOPRIGHT", 0, 0)
        panel:SetPoint("BOTTOMRIGHT", -10, 15)
        panel:Hide()

        local scrollFrame, content = createScrollPanel { elementID = "TabScrollPanel_" .. i, parent = panel }
        panel.scrollFrame = scrollFrame
        panel.content = content
        panel.UpdateLayout = function() buildFuncs[i](panel) end

        table.insert(panels, panel)
    end

    selectTab(activeTab)

    return {
        panels = panels,
        selectTab = selectTab,
        getActiveTab = function() return activeTab end
    }
end

-------------------------------------------------------------------------------
-- MAIN CONFIG WINDOW
-------------------------------------------------------------------------------

local function createConfigFrame()
    if ascensionBars.configFrame then return end

    ascensionBars.configFrame = CreateFrame("Frame", "AscensionBarsConfigFrame", UIParent, "BackdropTemplate")
    local configFrame = ascensionBars.configFrame
    configFrame:SetSize(ascensionBars.normalWidth or 750, ascensionBars.normalHeight or 500)
    configFrame:SetPoint("CENTER")
    configFrame:SetMovable(true)
    configFrame:EnableMouse(true)
    configFrame:RegisterForDrag("LeftButton")
    configFrame:SetFrameStrata("HIGH")
    configFrame:SetScript("OnDragStart", function(self) self:StartMoving() end)
    configFrame:SetScript("OnDragStop", function(self) self:StopMovingOrSizing() end)

    configFrame:SetBackdrop {
        bgFile = files.bgfile,
        edgeFile = files.edgefile,
        edgeSize = 16,
        insets = { left = 4, right = 4, top = 4, bottom = 4 }
    }
    configFrame:SetBackdropColor(unpack(colors.background_dark))
    configFrame:SetBackdropBorderColor(unpack(colors.surface_highlight))

    configFrame:SetResizable(true)
    configFrame:SetResizeBounds(500, 400, 1200, 1000)

    local resizeButton = CreateFrame("Button", nil, configFrame)
    resizeButton:SetSize(16, 16)
    resizeButton:SetPoint("BOTTOMRIGHT", -4, 4)
    resizeButton:SetNormalTexture("Interface\\ChatFrame\\UI-ChatIM-SizeGrabber-Up")
    resizeButton:SetHighlightTexture("Interface\\ChatFrame\\UI-ChatIM-SizeGrabber-Highlight")
    resizeButton:SetPushedTexture("Interface\\ChatFrame\\UI-ChatIM-SizeGrabber-Down")
    resizeButton:SetScript("OnMouseDown", function(_, btn)
        if btn == "LeftButton" then configFrame:StartSizing("BOTTOMRIGHT") end
    end)
    resizeButton:SetScript("OnMouseUp", function() configFrame:StopMovingOrSizing() end)

    local title = configFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalHuge")
    title:SetPoint("TOPLEFT", menuStyle.titleLeft, menuStyle.titleTop)
    title:SetText(Locales["ADDON_NAME"])
    title:SetTextColor(unpack(colors.gold))

    local titleSeparator = configFrame:CreateTexture(nil, "ARTWORK")
    titleSeparator:SetHeight(1)
    titleSeparator:SetPoint("TOPLEFT", configFrame, "TOPLEFT", menuStyle.separatorSideMargin, menuStyle.separatorTop)
    titleSeparator:SetPoint("RIGHT", configFrame, "RIGHT", -menuStyle.separatorSideMargin, 0)
    titleSeparator:SetColorTexture(unpack(colors.surface_highlight))

    local closeButton = CreateFrame("Button", nil, configFrame, "UIPanelCloseButton")
    closeButton:SetPoint("TOPRIGHT", 0, 0)
    closeButton:SetScript("OnClick", function() configFrame:Hide() end)

    local minimizeButton = CreateFrame("Button", nil, configFrame)
    minimizeButton:SetSize(24, 24)
    minimizeButton:SetPoint("RIGHT", closeButton, "LEFT", -5, 0)
    minimizeButton:SetNormalTexture(files.minimize)
    minimizeButton:SetHighlightTexture(files.minimize, "ADD")

    local function updateMinimizeState()
        if ascensionBars.isMinimized then
            configFrame:SetSize(300, 40)
            for _, child in ipairs({ configFrame:GetChildren() }) do
                if child ~= minimizeButton and child ~= closeButton then child:Hide() end
            end
            minimizeButton:SetNormalTexture(files.maximize)
        else
            configFrame:SetSize(ascensionBars.normalWidth or 750, ascensionBars.normalHeight or 500)
            for _, child in ipairs({ configFrame:GetChildren() }) do child:Show() end
            ascensionBars.configTabs.selectTab(ascensionBars.activeTab or 1)
            minimizeButton:SetNormalTexture(files.minimize)
        end
    end

    minimizeButton:SetScript("OnClick", function()
        ascensionBars.isMinimized = not ascensionBars.isMinimized
        updateMinimizeState()
    end)

    local resetButton = CreateFrame("Button", nil, configFrame, "UIPanelButtonTemplate")
    resetButton:SetSize(130, 24)
    resetButton:SetPoint("BOTTOMLEFT", menuStyle.contentPadding, 40)
    resetButton:SetText((Locales and Locales["FACTION_STANDINGS_RESET"]) or "Reset Defaults")
    resetButton:SetScript("OnClick", function()
        if ascensionBars and ascensionBars.db then
            ascensionBars.db:ResetProfile()
            if ascensionBars.updateDisplay then ascensionBars:updateDisplay() end
            ascensionBars.configTabs.selectTab(1)
        end
    end)

    local configModeCheck = CreateFrame("CheckButton", nil, configFrame, "UICheckButtonTemplate")
    configModeCheck:SetPoint("BOTTOMLEFT", menuStyle.contentPadding, 12)
    configModeCheck:SetSize(menuStyle.checkboxSize, menuStyle.checkboxSize)
    configModeCheck.text = configModeCheck:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    configModeCheck.text:SetPoint("LEFT", configModeCheck, "RIGHT", 5, 0)
    configModeCheck.text:SetText((Locales and Locales["CONFIG_MODE"]) or "Config Mode")
    configModeCheck:SetScript("OnShow", function(self)
        if ascensionBars and ascensionBars.state then self:SetChecked(ascensionBars.state.isConfigMode) end
    end)
    configModeCheck:SetScript("OnClick", function(self)
        if ascensionBars and ascensionBars.state then
            ascensionBars.state.isConfigMode = self:GetChecked()
            if ascensionBars.updateDisplay then ascensionBars:updateDisplay() end
        end
    end)

    local tabNames = {
        Locales["TAB_BARS_LAYOUT"],
        Locales["TAB_TEXT_LAYOUT"],
        Locales["TAB_BEHAVIOR"],
        Locales["TAB_COLORS"],
        Locales["TAB_PARAGON_ALERTS"]
    }
    local buildFuncs = { buildLayoutTab, buildAppearanceTab, buildBehaviorTab, buildColorsTab, buildParagonTab }

    ascensionBars.configTabs = createTabbedInterface(configFrame, tabNames, buildFuncs, 1)

    configFrame:Hide()
end

function ascensionBars:refreshConfigUI()
    if self.configFrame then
        local wasShown = self.configFrame:IsShown()
        local currentTab = self.configTabs and self.configTabs.getActiveTab() or 1

        self.configFrame:Hide()
        self.configFrame = nil
        self.configTabs = nil

        self:toggleConfig()

        if wasShown then
            self.configTabs.selectTab(currentTab)
        else
            self.configFrame:Hide()
        end
    end
end

function ascensionBars:refreshConfig() self:refreshConfigUI() end

function ascensionBars:toggleConfig()
    if not self.configFrame then createConfigFrame() end
    if self.configFrame then
        if self.configFrame:IsShown() then self.configFrame:Hide() else self.configFrame:Show() end
    end
end

-------------------------------------------------------------------------------
-- BLIZZARD OPTIONS PANEL
-------------------------------------------------------------------------------

local blizzardPanel = CreateFrame("Frame", "AscensionBars_BlizPanel", UIParent, "BackdropTemplate")
blizzardPanel.name = Locales["ADDON_NAME"] or addonName
blizzardPanel:Hide()

local blizzardConfigFrame
local toggleButton

local blizzardPanel = CreateFrame("Frame", "AscensionBars_BlizPanel", UIParent, "BackdropTemplate")
blizzardPanel.name = Locales["ADDON_NAME"] or addonName
blizzardPanel:Hide()

local blizzardConfigFrame
local toggleButton
local blizzardTabContainer -- new container for the tabbed interface

local function buildBlizUI()
    -- Create the main container if it doesn't exist
    if not blizzardConfigFrame then
        blizzardConfigFrame = CreateFrame("Frame", nil, blizzardPanel)
        blizzardConfigFrame:SetPoint("TOPLEFT", 0, 0)
        blizzardConfigFrame:SetPoint("BOTTOMRIGHT", 0, 0)
    end

    -- Create or reposition the toggle button (top‑right corner)
    if not toggleButton then
        toggleButton = CreateFrame("Button", nil, blizzardConfigFrame, "UIPanelButtonTemplate")
        toggleButton:SetSize(120, 22)
        toggleButton:SetText("Toggle Config Window")
        toggleButton:SetPoint("TOPRIGHT", blizzardConfigFrame, "TOPRIGHT", -10, -10)
        toggleButton:SetScript("OnClick", function()
            ascensionBars:toggleConfig()
        end)
    else
        toggleButton:Show()
        toggleButton:ClearAllPoints()
        toggleButton:SetPoint("TOPRIGHT", blizzardConfigFrame, "TOPRIGHT", -10, -10)
    end

    -- Create a container for the tabbed content that sits below the button
    if not blizzardTabContainer then
        blizzardTabContainer = CreateFrame("Frame", nil, blizzardConfigFrame)
    end
    blizzardTabContainer:SetPoint("TOPLEFT", blizzardConfigFrame, "TOPLEFT", 0, -35)
    blizzardTabContainer:SetPoint("BOTTOMRIGHT", blizzardConfigFrame, "BOTTOMRIGHT", 0, 0)

    for _, child in ipairs({ blizzardTabContainer:GetChildren() }) do
        child:Hide()
        child:ClearAllPoints()
    end

    local tabNames = {
        Locales["TAB_BARS_LAYOUT"],
        Locales["TAB_TEXT_LAYOUT"],
        Locales["TAB_BEHAVIOR"],
        Locales["TAB_COLORS"],
        Locales["TAB_PARAGON_ALERTS"]
    }
    local buildFuncs = { buildLayoutTab, buildAppearanceTab, buildBehaviorTab, buildColorsTab, buildParagonTab }

    _ = createTabbedInterface(blizzardTabContainer, tabNames, buildFuncs, 1)
end

blizzardPanel:SetScript("OnShow", buildBlizUI)

function blizzardPanel:refresh() buildBlizUI() end

local function registerBlizzardOptions()
    if Settings and Settings.RegisterCanvasLayoutCategory then
        local category, layout = Settings.RegisterCanvasLayoutCategory(blizzardPanel, blizzardPanel.name)
        Settings.RegisterAddOnCategory(category)
    else
        local addCategory = _G["InterfaceOptions_AddCategory"]
        if addCategory then addCategory(blizzardPanel) end
    end
end

if ascensionBars:IsEnabled() or (ascensionBars.db) then
    registerBlizzardOptions()
else
    local oldInit = ascensionBars.OnInitialize
    ascensionBars.OnInitialize = function(self)
        if type(oldInit) == "function" then oldInit(self) end
        registerBlizzardOptions()
    end
end
