local OneWoW_GUI = LibStub("OneWoW_GUI-1.0", true)
if not OneWoW_GUI then return end

local CreateFrame = CreateFrame

local Constants = OneWoW_GUI.Constants

function OneWoW_GUI:CreateFilterBar(parent, config)
    config = config or {}
    local height = config.height or 40
    local anchorBelow = config.anchorBelow
    local offset = config.offset or -5

    local bar = CreateFrame("Frame", nil, parent, "BackdropTemplate")
    if anchorBelow then
        bar:SetPoint("TOPLEFT", anchorBelow, "BOTTOMLEFT", 0, offset)
        bar:SetPoint("TOPRIGHT", anchorBelow, "BOTTOMRIGHT", 0, offset)
    else
        bar:SetPoint("TOPLEFT", parent, "TOPLEFT", 5, offset)
        bar:SetPoint("TOPRIGHT", parent, "TOPRIGHT", -5, offset)
    end
    bar:SetHeight(height)
    bar:SetBackdrop(Constants.BACKDROP_INNER_NO_INSETS)
    bar:SetBackdropColor(OneWoW_GUI:GetThemeColor("BG_SECONDARY"))
    bar:SetBackdropBorderColor(OneWoW_GUI:GetThemeColor("BORDER_DEFAULT"))

    return bar
end

function OneWoW_GUI:CreateSortControls(parent, options)
    options = options or {}
    local sortFields   = options.sortFields   or {}
    local defaultField = options.defaultField or (sortFields[1] and sortFields[1].key) or ""
    local defaultAsc   = options.defaultAsc ~= false
    local onChange     = options.onChange or function(field, ascending) end
    local dropWidth    = options.dropdownWidth or 110

    local state = { field = defaultField, ascending = defaultAsc }

    local dropdown, textFS = self:CreateDropdown(parent, { width = dropWidth, height = 25 })

    local function RefreshDropText()
        for _, f in ipairs(sortFields) do
            if f.key == state.field then
                textFS:SetText(f.label)
                break
            end
        end
    end
    RefreshDropText()
    dropdown._activeValue = defaultField

    self:AttachFilterMenu(dropdown, {
        searchable = false,
        buildItems = function()
            local items = {}
            for _, f in ipairs(sortFields) do
                items[#items + 1] = { text = f.label, value = f.key }
            end
            return items
        end,
        onSelect = function(value, label)
            state.field = value
            textFS:SetText(label)
            dropdown._activeValue = value
            onChange(state.field, state.ascending)
        end,
        getActiveValue = function() return state.field end,
    })

    local dirBtn = CreateFrame("Button", nil, parent)
    dirBtn:SetSize(24, 25)

    local function UpdateDirAtlas()
        local atlas = state.ascending and "common-button-collapseExpand-up" or "common-button-collapseExpand-down"
        dirBtn:SetNormalAtlas(atlas)
        dirBtn:SetPushedAtlas(atlas)
        dirBtn:SetHighlightAtlas(atlas)
        if dirBtn:GetHighlightTexture() then
            dirBtn:GetHighlightTexture():SetAlpha(0.5)
        end
    end
    UpdateDirAtlas()

    dirBtn:SetScript("OnClick", function()
        state.ascending = not state.ascending
        UpdateDirAtlas()
        onChange(state.field, state.ascending)
    end)

    local handle = { dropdown = dropdown, dirBtn = dirBtn }

    function handle:GetSort()
        return state.field, state.ascending
    end

    function handle:SetSort(field, ascending)
        state.field     = field
        state.ascending = ascending ~= false
        dropdown._activeValue = field
        RefreshDropText()
        UpdateDirAtlas()
    end

    return handle
end

function OneWoW_GUI:CreateHeader(parent, options)
    options = options or {}
    local text = options.text or ""
    local yOffset = options.yOffset or -OneWoW_GUI:GetSpacing("MD")
    local header = parent:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    OneWoW_GUI:SetFontBaseSize(header, 16)
    OneWoW_GUI:SafeSetFont(header, OneWoW_GUI:GetFont(), 16)
    header:SetPoint("TOPLEFT", OneWoW_GUI:GetSpacing("MD"), yOffset)
    header:SetText(text)
    header:SetTextColor(OneWoW_GUI:GetThemeColor("ACCENT_PRIMARY"))
    return header
end

function OneWoW_GUI:CreateDivider(parent, options)
    options = options or {}
    local yOffset = options.yOffset or 0
    local divider = parent:CreateTexture(nil, "ARTWORK")
    divider:SetHeight(1)
    divider:SetPoint("LEFT", OneWoW_GUI:GetSpacing("MD"), 0)
    divider:SetPoint("RIGHT", -OneWoW_GUI:GetSpacing("MD"), 0)
    divider:SetPoint("TOP", 0, yOffset)
    divider:SetColorTexture(OneWoW_GUI:GetThemeColor("BORDER_SUBTLE"))
    return divider
end

function OneWoW_GUI:CreateSection(parent, options)
    options = options or {}
    local title = options.title or ""
    local yOffset = options.yOffset or 0
    local header = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    OneWoW_GUI:SetFontBaseSize(header, 12)
    OneWoW_GUI:SafeSetFont(header, OneWoW_GUI:GetFont(), 12)
    header:SetPoint("TOPLEFT", parent, "TOPLEFT", OneWoW_GUI:GetSpacing("MD"), yOffset)
    header:SetText(title)
    header:SetTextColor(OneWoW_GUI:GetThemeColor("ACCENT_SECONDARY"))
    yOffset = yOffset - header:GetStringHeight() - 6

    local divider = parent:CreateTexture(nil, "ARTWORK")
    divider:SetHeight(1)
    divider:SetPoint("TOPLEFT", parent, "TOPLEFT", OneWoW_GUI:GetSpacing("MD"), yOffset)
    divider:SetPoint("TOPRIGHT", parent, "TOPRIGHT", -OneWoW_GUI:GetSpacing("MD"), yOffset)
    divider:SetColorTexture(OneWoW_GUI:GetThemeColor("BORDER_SUBTLE"))
    yOffset = yOffset - 10

    return yOffset
end

function OneWoW_GUI:CreateTitleBar(parent, options)
    options = options or {}
    local title = options.title or ""
    local height = options.height or Constants.GUI.TITLEBAR_HEIGHT
    local onClose = options.onClose
    local showBrand = options.showBrand

    local titleBg = CreateFrame("Frame", nil, parent, "BackdropTemplate")
    titleBg:SetPoint("TOPLEFT", parent, "TOPLEFT", 1, -1)
    titleBg:SetPoint("TOPRIGHT", parent, "TOPRIGHT", -1, -1)
    titleBg:SetHeight(height)
    titleBg:SetBackdrop(Constants.BACKDROP_SIMPLE)
    titleBg:SetBackdropColor(OneWoW_GUI:GetThemeColor("TITLEBAR_BG"))
    titleBg:SetFrameLevel(parent:GetFrameLevel() + 1)

    titleBg._titleText = titleBg:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    OneWoW_GUI:SetFontBaseSize(titleBg._titleText, 12)
    OneWoW_GUI:SafeSetFont(titleBg._titleText, OneWoW_GUI:GetFont(), 12)

    if showBrand then
        local factionTheme = options.factionTheme
        if not factionTheme then
            factionTheme = (self.GetSetting and self:GetSetting("minimap.theme")) or "horde"
        end
        local brandIcon = titleBg:CreateTexture(nil, "OVERLAY")
        brandIcon:SetSize(14, 14)
        brandIcon:SetPoint("LEFT", titleBg, "LEFT", OneWoW_GUI:GetSpacing("SM"), 0)
        brandIcon:SetTexture(self:GetBrandIcon(factionTheme))
        titleBg.brandIcon = brandIcon

        self:RegisterSettingsCallback("OnIconThemeChanged", titleBg, function(owner)
            if owner.brandIcon and owner:IsVisible() then
                owner.brandIcon:SetTexture(OneWoW_GUI:GetBrandIcon(
                    (OneWoW_GUI.GetSetting and OneWoW_GUI:GetSetting("minimap.theme")) or "horde"))
            end
        end)

        local brandText = titleBg:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        OneWoW_GUI:SetFontBaseSize(brandText, 12)
        OneWoW_GUI:SafeSetFont(brandText, OneWoW_GUI:GetFont(), 12)
        brandText:SetPoint("LEFT", brandIcon, "RIGHT", 4, 0)
        brandText:SetText("OneWoW")
        brandText:SetTextColor(OneWoW_GUI:GetThemeColor("ACCENT_PRIMARY"))

        titleBg._titleText:SetPoint("CENTER", titleBg, "CENTER", 0, 0)
        titleBg._titleText:SetText(title)
        titleBg._titleText:SetTextColor(OneWoW_GUI:GetThemeColor("TEXT_PRIMARY"))
    else
        titleBg._titleText:SetPoint("LEFT", titleBg, "LEFT", OneWoW_GUI:GetSpacing("MD"), 0)
        titleBg._titleText:SetText(title)
        titleBg._titleText:SetTextColor(OneWoW_GUI:GetThemeColor("ACCENT_PRIMARY"))
    end

    if onClose then
        local closeBtn = self:CreateButton(titleBg, { text = "X", width = 20, height = 20 })
        closeBtn:SetPoint("RIGHT", titleBg, "RIGHT", -OneWoW_GUI:GetSpacing("XS") / 2, 0)
        closeBtn:SetScript("OnClick", onClose)
        titleBg._closeBtn = closeBtn
    end

    return titleBg
end

function OneWoW_GUI:CreateSectionHeader(parent, options)
    options = options or {}
    local title = options.title or ""
    local yOffset = options.yOffset or 0
    local section = CreateFrame("Frame", nil, parent, "BackdropTemplate")
    section:SetPoint("TOPLEFT", 0, yOffset)
    section:SetPoint("TOPRIGHT", 0, yOffset)
    section:SetHeight(30)
    section:SetBackdrop(Constants.BACKDROP_INNER_NO_INSETS)
    section:SetBackdropColor(OneWoW_GUI:GetThemeColor("BG_SECONDARY"))
    section:SetBackdropBorderColor(OneWoW_GUI:GetThemeColor("BORDER_SUBTLE"))

    local titleText = section:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    OneWoW_GUI:SetFontBaseSize(titleText, 12)
    OneWoW_GUI:SafeSetFont(titleText, OneWoW_GUI:GetFont(), 12)
    titleText:SetPoint("LEFT", 12, 0)
    titleText:SetText(title)
    titleText:SetTextColor(OneWoW_GUI:GetThemeColor("ACCENT_PRIMARY"))

    local sectionHeight = math.max(30, titleText:GetStringHeight() + 14)
    section:SetHeight(sectionHeight)
    section.bottomY = yOffset - sectionHeight

    return section
end

function OneWoW_GUI:CreateHeroPanel(parent, options)
    options = options or {}
    local height = options.height or Constants.GUI.HERO_PANEL_HEIGHT
    local insetX = options.insetX or OneWoW_GUI:GetSpacing("MD")
    local yOffset = options.yOffset or -OneWoW_GUI:GetSpacing("MD")
    local panel = self:CreateFrame(parent, {
        height = height,
        backdrop = Constants.BACKDROP_INNER_NO_INSETS,
        bgColor = options.bgColor or "BG_SECONDARY",
        borderColor = options.borderColor or "BORDER_ACCENT",
    })
    panel:SetPoint("TOPLEFT", parent, "TOPLEFT", insetX, yOffset)
    panel:SetPoint("TOPRIGHT", parent, "TOPRIGHT", -insetX, yOffset)

    local accent = panel:CreateTexture(nil, "ARTWORK")
    accent:SetPoint("TOPLEFT", panel, "TOPLEFT", 0, 0)
    accent:SetPoint("BOTTOMLEFT", panel, "BOTTOMLEFT", 0, 0)
    accent:SetWidth(4)
    accent:SetColorTexture(OneWoW_GUI:GetThemeColor(options.accentColor or "ACCENT_PRIMARY"))
    panel.accent = accent

    local iconSize = options.iconSize or 48
    local iconFrame = self:CreateFrame(panel, {
        width = iconSize + 10,
        height = iconSize + 10,
        backdrop = Constants.BACKDROP_INNER_NO_INSETS,
        bgColor = "BG_TERTIARY",
        borderColor = "BORDER_ACCENT",
    })
    iconFrame:SetPoint("TOPLEFT", panel, "TOPLEFT", OneWoW_GUI:GetSpacing("LG"), -OneWoW_GUI:GetSpacing("LG"))

    local icon = iconFrame:CreateTexture(nil, "ARTWORK")
    icon:SetSize(iconSize, iconSize)
    icon:SetPoint("CENTER", iconFrame, "CENTER", 0, 0)
    if options.iconAtlas then
        icon:SetAtlas(options.iconAtlas)
    else
        icon:SetTexture(options.iconTexture or self:GetBrandIcon())
    end
    panel.icon = icon

    local title = self:CreateFS(panel, options.titleSize or 18)
    title:SetPoint("TOPLEFT", iconFrame, "TOPRIGHT", OneWoW_GUI:GetSpacing("MD"), -2)
    title:SetPoint("TOPRIGHT", panel, "TOPRIGHT", -OneWoW_GUI:GetSpacing("LG"), -2)
    title:SetJustifyH("LEFT")
    title:SetText(options.title or "")
    title:SetTextColor(OneWoW_GUI:GetThemeColor("ACCENT_PRIMARY"))
    panel.title = title

    local subtitle = self:CreateFS(panel, options.subtitleSize or 12)
    subtitle:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -4)
    subtitle:SetPoint("TOPRIGHT", title, "BOTTOMRIGHT", 0, -4)
    subtitle:SetJustifyH("LEFT")
    subtitle:SetText(options.subtitle or "")
    subtitle:SetTextColor(OneWoW_GUI:GetThemeColor("TEXT_PRIMARY"))
    panel.subtitle = subtitle

    local description = self:CreateFS(panel, options.descriptionSize or 11)
    description:SetPoint("TOPLEFT", subtitle, "BOTTOMLEFT", 0, -6)
    description:SetPoint("TOPRIGHT", subtitle, "BOTTOMRIGHT", 0, -6)
    description:SetJustifyH("LEFT")
    description:SetWordWrap(true)
    description:SetText(options.description or "")
    description:SetTextColor(OneWoW_GUI:GetThemeColor("TEXT_SECONDARY"))
    panel.description = description

    if options.calloutText and options.calloutText ~= "" then
        local callout = self:CreateFrame(panel, {
            width = options.calloutWidth or 185,
            height = Constants.GUI.BADGE_HEIGHT,
            backdrop = Constants.BACKDROP_INNER_NO_INSETS,
            bgColor = "BG_ACTIVE",
            borderColor = "BORDER_ACCENT",
        })
        callout:SetPoint("BOTTOMRIGHT", panel, "BOTTOMRIGHT", -OneWoW_GUI:GetSpacing("MD"), OneWoW_GUI:GetSpacing("MD"))
        callout.text = self:CreateFS(callout, 10)
        callout.text:SetPoint("CENTER", callout, "CENTER", 0, 0)
        callout.text:SetText(options.calloutText)
        callout.text:SetTextColor(OneWoW_GUI:GetThemeColor("TEXT_ACCENT"))
        panel.callout = callout
    end

    panel.bottomY = yOffset - height
    return panel
end

function OneWoW_GUI:CreateSummaryStrip(parent, options)
    options = options or {}
    local height = options.height or Constants.GUI.SUMMARY_STRIP_HEIGHT
    local insetX = options.insetX or OneWoW_GUI:GetSpacing("MD")
    local yOffset = options.yOffset or 0
    local items = options.items or {}
    local strip = self:CreateFrame(parent, {
        height = height,
        backdrop = Constants.BACKDROP_INNER_NO_INSETS,
        bgColor = options.bgColor or "BG_PRIMARY",
        borderColor = options.borderColor or "BORDER_SUBTLE",
    })
    strip:SetPoint("TOPLEFT", parent, "TOPLEFT", insetX, yOffset)
    strip:SetPoint("TOPRIGHT", parent, "TOPRIGHT", -insetX, yOffset)
    strip.itemBoxes = {}

    for i, item in ipairs(items) do
        local box = self:CreateFrame(strip, {
            backdrop = Constants.BACKDROP_INNER_NO_INSETS,
            bgColor = "BG_TERTIARY",
            borderColor = "BORDER_SUBTLE",
        })
        box.value = self:CreateFS(box, item.valueSize or 15)
        box.value:SetPoint("TOPLEFT", box, "TOPLEFT", OneWoW_GUI:GetSpacing("SM"), -7)
        box.value:SetPoint("TOPRIGHT", box, "TOPRIGHT", -OneWoW_GUI:GetSpacing("SM"), -7)
        box.value:SetJustifyH("LEFT")
        box.value:SetText(item.value or "")
        box.value:SetTextColor(OneWoW_GUI:GetThemeColor(item.valueColor or "TEXT_ACCENT"))

        box.label = self:CreateFS(box, item.labelSize or 10)
        box.label:SetPoint("TOPLEFT", box.value, "BOTTOMLEFT", 0, -4)
        box.label:SetPoint("TOPRIGHT", box.value, "BOTTOMRIGHT", 0, -4)
        box.label:SetJustifyH("LEFT")
        box.label:SetText(item.label or "")
        box.label:SetTextColor(OneWoW_GUI:GetThemeColor("TEXT_SECONDARY"))

        strip.itemBoxes[i] = box
    end

    local function LayoutBoxes()
        local count = #strip.itemBoxes
        if count == 0 then return end
        local width = strip:GetWidth()
        if width <= 0 then return end
        local gap = OneWoW_GUI:GetSpacing("SM")
        local innerX = OneWoW_GUI:GetSpacing("SM")
        local boxWidth = (width - (innerX * 2) - (gap * (count - 1))) / count
        for i, box in ipairs(strip.itemBoxes) do
            box:ClearAllPoints()
            box:SetPoint("TOPLEFT", strip, "TOPLEFT", innerX + ((i - 1) * (boxWidth + gap)), -OneWoW_GUI:GetSpacing("SM"))
            box:SetSize(boxWidth, height - (OneWoW_GUI:GetSpacing("SM") * 2))
        end
    end

    strip:SetScript("OnSizeChanged", LayoutBoxes)
    C_Timer.After(0, LayoutBoxes)

    function strip:SetItemValue(index, value)
        local box = self.itemBoxes and self.itemBoxes[index]
        if box and box.value then
            box.value:SetText(value or "")
        end
    end

    strip.bottomY = yOffset - height
    return strip
end

function OneWoW_GUI:CreateSelectableCard(parent, options)
    options = options or {}
    local height = options.height or Constants.GUI.SELECTABLE_CARD_HEIGHT
    local card = CreateFrame("Button", options.name, parent, "BackdropTemplate")
    card:SetHeight(height)
    card:SetBackdrop(Constants.BACKDROP_INNER_NO_INSETS)
    card:RegisterForClicks("LeftButtonUp")
    card._checked = options.checked and true or false
    card._onToggle = options.onToggle

    local selectedAccent = card:CreateTexture(nil, "ARTWORK")
    selectedAccent:SetPoint("TOPLEFT", card, "TOPLEFT", 0, 0)
    selectedAccent:SetPoint("BOTTOMLEFT", card, "BOTTOMLEFT", 0, 0)
    selectedAccent:SetWidth(4)
    selectedAccent:SetColorTexture(OneWoW_GUI:GetThemeColor("ACCENT_PRIMARY"))
    card.selectedAccent = selectedAccent

    local iconSize = options.iconSize or Constants.GUI.SELECTABLE_CARD_ICON_SIZE
    local iconFrame = self:CreateFrame(card, {
        width = iconSize + 8,
        height = iconSize + 8,
        backdrop = Constants.BACKDROP_INNER_NO_INSETS,
        bgColor = "BG_TERTIARY",
        borderColor = "BORDER_SUBTLE",
    })
    iconFrame:SetPoint("LEFT", card, "LEFT", OneWoW_GUI:GetSpacing("MD"), 0)
    card.iconFrame = iconFrame

    local icon = iconFrame:CreateTexture(nil, "ARTWORK")
    icon:SetSize(iconSize, iconSize)
    icon:SetPoint("CENTER", iconFrame, "CENTER", 0, 0)
    if options.iconAtlas then
        icon:SetAtlas(options.iconAtlas)
    else
        icon:SetTexture(options.iconTexture or self:GetBrandIcon())
    end
    card.icon = icon

    local check = self:CreateCheckbox(card, {
        label = "",
        checked = card._checked,
        onClick = function(self)
            card:SetChecked(self:GetChecked() and true or false)
        end,
    })
    check:SetPoint("RIGHT", card, "RIGHT", -OneWoW_GUI:GetSpacing("MD"), 0)
    card.checkbox = check

    local title = self:CreateFS(card, options.titleSize or 13)
    title:SetPoint("TOPLEFT", iconFrame, "TOPRIGHT", OneWoW_GUI:GetSpacing("MD"), -5)
    title:SetPoint("RIGHT", check, "LEFT", -OneWoW_GUI:GetSpacing("MD"), 0)
    title:SetJustifyH("LEFT")
    title:SetText(options.title or "")
    card.title = title

    local summary = self:CreateFS(card, options.summarySize or 11)
    summary:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -5)
    summary:SetPoint("RIGHT", check, "LEFT", -OneWoW_GUI:GetSpacing("MD"), 0)
    summary:SetJustifyH("LEFT")
    summary:SetWordWrap(true)
    summary:SetText(options.summary or "")
    card.summary = summary

    if options.badgeText and options.badgeText ~= "" then
        local badgeMeasure = card:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        OneWoW_GUI:SafeSetFont(badgeMeasure, OneWoW_GUI:GetFont(), 9)
        badgeMeasure:SetText(options.badgeText)
        local badgeWidth = math.max(options.badgeWidth or 0, badgeMeasure:GetStringWidth() + 18)
        badgeMeasure:Hide()
        badgeMeasure:SetParent(nil)

        local badge = self:CreateFrame(card, {
            width = badgeWidth,
            height = Constants.GUI.BADGE_HEIGHT,
            backdrop = Constants.BACKDROP_INNER_NO_INSETS,
            bgColor = "BG_TERTIARY",
            borderColor = "BORDER_SUBTLE",
        })
        badge:SetPoint("TOPRIGHT", check, "LEFT", -OneWoW_GUI:GetSpacing("MD"), -5)
        badge.text = self:CreateFS(badge, 9)
        badge.text:SetPoint("CENTER", badge, "CENTER", 0, 0)
        badge.text:SetText(options.badgeText)
        card.badge = badge
        title:SetPoint("RIGHT", badge, "LEFT", -OneWoW_GUI:GetSpacing("SM"), 0)
    end

    local function ApplyVisual(self, hover)
        if self._checked then
            self:SetBackdropColor(OneWoW_GUI:GetThemeColor(hover and "BG_HOVER" or "BG_ACTIVE"))
            self:SetBackdropBorderColor(OneWoW_GUI:GetThemeColor(hover and "BORDER_FOCUS" or "BORDER_ACCENT"))
            self.selectedAccent:Show()
            self.title:SetTextColor(OneWoW_GUI:GetThemeColor("TEXT_ACCENT"))
            self.summary:SetTextColor(OneWoW_GUI:GetThemeColor("TEXT_SECONDARY"))
            self.icon:SetDesaturated(false)
            self.icon:SetAlpha(1)
            self.iconFrame:SetBackdropBorderColor(OneWoW_GUI:GetThemeColor("BORDER_ACCENT"))
            if self.badge then
                self.badge:SetBackdropBorderColor(OneWoW_GUI:GetThemeColor("BORDER_ACCENT"))
                self.badge.text:SetTextColor(OneWoW_GUI:GetThemeColor("TEXT_ACCENT"))
            end
        else
            self:SetBackdropColor(OneWoW_GUI:GetThemeColor(hover and "BG_HOVER" or "BG_SECONDARY"))
            self:SetBackdropBorderColor(OneWoW_GUI:GetThemeColor(hover and "BORDER_FOCUS" or "BORDER_SUBTLE"))
            self.selectedAccent:Hide()
            self.title:SetTextColor(OneWoW_GUI:GetThemeColor(hover and "TEXT_ACCENT" or "TEXT_PRIMARY"))
            self.summary:SetTextColor(OneWoW_GUI:GetThemeColor("TEXT_MUTED"))
            self.icon:SetDesaturated(true)
            self.icon:SetAlpha(0.68)
            self.iconFrame:SetBackdropBorderColor(OneWoW_GUI:GetThemeColor("BORDER_SUBTLE"))
            if self.badge then
                self.badge:SetBackdropBorderColor(OneWoW_GUI:GetThemeColor("BORDER_SUBTLE"))
                self.badge.text:SetTextColor(OneWoW_GUI:GetThemeColor("TEXT_SECONDARY"))
            end
        end
    end

    function card:SetChecked(checked, silent)
        self._checked = checked and true or false
        self.checkbox:SetChecked(self._checked)
        ApplyVisual(self, self:IsMouseOver())
        if not silent and self._onToggle then
            self._onToggle(self, self._checked)
        end
    end

    function card:GetChecked()
        return self._checked
    end

    card:SetScript("OnEnter", function(self) ApplyVisual(self, true) end)
    card:SetScript("OnLeave", function(self) ApplyVisual(self, false) end)
    card:SetScript("OnClick", function(self)
        self:SetChecked(not self._checked)
    end)

    card:SetChecked(card._checked, true)
    return card
end

function OneWoW_GUI:CreateVerticalPaneResizer(options)
    options = options or {}
    local parent = options.parent
    local leftPanel = options.leftPanel
    local rightPanel = options.rightPanel
    if not parent or not leftPanel or not rightPanel then
        error("OneWoW_GUI:CreateVerticalPaneResizer requires parent, leftPanel, and rightPanel")
    end

    local floor = math.floor
    local ceil = math.ceil
    local min = math.min
    local max = math.max

    local dividerWidth = options.dividerWidth or 6
    local leftMinWidth = options.leftMinWidth or 200
    local rightMinWidth = options.rightMinWidth or 280
    local splitPadding = options.splitPadding
    if splitPadding == nil then
        splitPadding = dividerWidth + 10
    end
    local bottomOuterInset = options.bottomOuterInset or 5
    local rightOuterInset = options.rightOuterInset or 5
    local resizeCap = options.resizeCap or 0.95
    local mainFrame = options.mainFrame -- widened when drag would leave less than dynamic right reserve
    local getMinRightWidth = options.getMinRightWidth -- fun() -> number; used only to grow mainFrame (e.g. unbounded text width)
    local onWidthChanged = options.onWidthChanged -- fun(leftWidth) after mouse release (persist)
    local maxAutoGrowSteps = options.maxAutoGrowSteps or 12 -- max mainFrame widen attempts per drag tick; handles deferred layout width

    local function effectiveRightReserveForGrow()
        local r = rightMinWidth
        if getMinRightWidth then
            local n = getMinRightWidth()
            if type(n) == "number" and n > r then
                r = n
            end
        end
        return r
    end

    local divider = CreateFrame("Button", nil, parent)
    divider:SetWidth(dividerWidth)
    divider:SetPoint("TOPLEFT", leftPanel, "TOPRIGHT", 0, 0)
    divider:SetPoint("BOTTOM", parent, "BOTTOM", 0, bottomOuterInset)
    divider:EnableMouse(true)

    local dividerTex = divider:CreateTexture(nil, "OVERLAY")
    dividerTex:SetWidth(2)
    dividerTex:SetPoint("TOP", divider, "TOP", 0, 0)
    dividerTex:SetPoint("BOTTOM", divider, "BOTTOM", 0, 0)
    dividerTex:SetColorTexture(OneWoW_GUI:GetThemeColor("BORDER_DEFAULT"))

    divider:SetScript("OnEnter", function(self)
        dividerTex:SetColorTexture(OneWoW_GUI:GetThemeColor("ACCENT_PRIMARY"))
        SetCursor("UI_RESIZE_CURSOR")
    end)
    divider:SetScript("OnLeave", function(self)
        if not self._owPaneDragActive then
            dividerTex:SetColorTexture(OneWoW_GUI:GetThemeColor("BORDER_DEFAULT"))
            SetCursor(nil)
        end
    end)

    divider:SetScript("OnMouseDown", function(self, button)
        if button == "LeftButton" then
            self._owPaneDragActive = true
            local x = GetCursorPosition()
            self._owPaneStartCursorX = x / self:GetEffectiveScale()
            self._owPaneStartLeftW = leftPanel:GetWidth()
        end
    end)

    divider:SetScript("OnMouseUp", function(self, button)
        if button == "LeftButton" and self._owPaneDragActive then
            self._owPaneDragActive = false
            dividerTex:SetColorTexture(OneWoW_GUI:GetThemeColor("BORDER_DEFAULT"))
            SetCursor(nil)
            if onWidthChanged then
                onWidthChanged(leftPanel:GetWidth())
            end
        end
    end)

    divider:SetScript("OnUpdate", function(self)
        if not self._owPaneDragActive then return end
        local cursorX = GetCursorPosition() / self:GetEffectiveScale()
        local delta = cursorX - self._owPaneStartCursorX
        local desiredLeftWidth = max(leftMinWidth, self._owPaneStartLeftW + delta)

        local neededRightGrow = effectiveRightReserveForGrow()
        local screenMax = floor(GetScreenWidth() * resizeCap)
        -- Grow host until tab can fit desiredLeft + dynamic right reserve (handles layout lag with multiple steps).
        if mainFrame then
            for _ = 1, maxAutoGrowSteps do
                local tabW = parent:GetWidth()
                local minTabW = desiredLeftWidth + neededRightGrow + splitPadding
                if tabW >= minTabW then
                    break
                end
                local gap = minTabW - tabW
                local shortage = max(1, ceil(gap - 1e-6))
                local currentMainW = mainFrame:GetWidth()
                local newMainW = min(currentMainW + shortage, screenMax)
                if newMainW <= currentMainW then
                    break
                end
                mainFrame:SetWidth(newMainW)
            end
        end

        local tabWidth = parent:GetWidth()
        -- Clamp uses fixed rightMinWidth only
        local maxLeftWidth = tabWidth - rightMinWidth - splitPadding
        if maxLeftWidth < leftMinWidth then
            maxLeftWidth = leftMinWidth
        end
        local newLeftWidth = max(leftMinWidth, min(desiredLeftWidth, maxLeftWidth))
        leftPanel:SetWidth(newLeftWidth)
    end)

    rightPanel:ClearAllPoints()
    rightPanel:SetPoint("TOPLEFT", divider, "TOPRIGHT", 0, 0)
    rightPanel:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", -rightOuterInset, bottomOuterInset)

    parent:HookScript("OnSizeChanged", function(_, w)
        local maxLeftWidth = w - rightMinWidth - splitPadding
        if maxLeftWidth < leftMinWidth then
            maxLeftWidth = leftMinWidth
        end
        local currentLeftWidth = leftPanel:GetWidth()
        if currentLeftWidth > maxLeftWidth then
            leftPanel:SetWidth(maxLeftWidth)
        end
    end)

    return divider
end

function OneWoW_GUI:CreateHorizontalPaneResizer(options)
    options = options or {}
    local parent = options.parent
    local topPanel = options.topPanel
    local bottomPanel = options.bottomPanel
    if not parent or not topPanel or not bottomPanel then
        error("OneWoW_GUI:CreateHorizontalPaneResizer requires parent, topPanel, and bottomPanel")
    end

    local floor = math.floor
    local max = math.max
    local min = math.min

    local dividerHeight = options.dividerHeight or 6
    local topMinHeight = options.topMinHeight or 100
    local bottomMinHeight = options.bottomMinHeight or 60
    local onHeightChanged = options.onHeightChanged

    local divider = CreateFrame("Button", nil, parent)
    divider:SetHeight(dividerHeight)
    divider:SetPoint("TOPLEFT", topPanel, "BOTTOMLEFT", 0, 0)
    divider:SetPoint("RIGHT", parent, "RIGHT", 0, 0)
    divider:EnableMouse(true)

    local dividerTex = divider:CreateTexture(nil, "OVERLAY")
    dividerTex:SetHeight(2)
    dividerTex:SetPoint("LEFT", divider, "LEFT", 0, 0)
    dividerTex:SetPoint("RIGHT", divider, "RIGHT", 0, 0)
    dividerTex:SetColorTexture(OneWoW_GUI:GetThemeColor("BORDER_DEFAULT"))

    divider:SetScript("OnEnter", function(self)
        dividerTex:SetColorTexture(OneWoW_GUI:GetThemeColor("ACCENT_PRIMARY"))
        SetCursor("UI_RESIZE_CURSOR")
    end)
    divider:SetScript("OnLeave", function(self)
        if not self._owPaneDragActive then
            dividerTex:SetColorTexture(OneWoW_GUI:GetThemeColor("BORDER_DEFAULT"))
            SetCursor(nil)
        end
    end)

    divider:SetScript("OnMouseDown", function(self, button)
        if button == "LeftButton" then
            self._owPaneDragActive = true
            local _, y = GetCursorPosition()
            self._owPaneStartCursorY = y / self:GetEffectiveScale()
            self._owPaneStartTopH = topPanel:GetHeight()
        end
    end)

    divider:SetScript("OnMouseUp", function(self, button)
        if button == "LeftButton" and self._owPaneDragActive then
            self._owPaneDragActive = false
            dividerTex:SetColorTexture(OneWoW_GUI:GetThemeColor("BORDER_DEFAULT"))
            SetCursor(nil)
            if onHeightChanged then
                onHeightChanged(bottomPanel:GetHeight())
            end
        end
    end)

    divider:SetScript("OnUpdate", function(self)
        if not self._owPaneDragActive then return end
        local _, cursorY = GetCursorPosition()
        cursorY = cursorY / self:GetEffectiveScale()
        local delta = self._owPaneStartCursorY - cursorY
        local desiredTopH = max(topMinHeight, self._owPaneStartTopH + delta)

        local totalH = parent:GetHeight()
        local maxTopH = totalH - bottomMinHeight - dividerHeight
        if maxTopH < topMinHeight then maxTopH = topMinHeight end
        local newTopH = max(topMinHeight, min(desiredTopH, maxTopH))
        topPanel:SetHeight(newTopH)
    end)

    bottomPanel:ClearAllPoints()
    bottomPanel:SetPoint("TOPLEFT", divider, "BOTTOMLEFT", 0, 0)
    bottomPanel:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", 0, 0)

    parent:HookScript("OnSizeChanged", function(_, _, h)
        if not h then return end
        local maxTopH = h - bottomMinHeight - dividerHeight
        if maxTopH < topMinHeight then maxTopH = topMinHeight end
        local currentTopH = topPanel:GetHeight()
        if currentTopH > maxTopH then
            topPanel:SetHeight(maxTopH)
        end
    end)

    return divider
end
