local addonName, RSTATS = ...

-- Rated Stats: Settings (Retail Settings UI)
-- Uses Blizzard's Settings API (Dragonflight+).

local CATEGORY_NAME = "Rated Stats"
local RS_COLOR = "|cffb69e86"
local function RSPrint(msg)
    print(RS_COLOR .. msg .. "|r")
end

local function GetPlayerKey()
    return UnitName("player") .. "-" .. GetRealmName()
end

-- "Update available" detection (Details/BGE style):
-- You cannot check CurseForge from inside WoW. You can only compare versions seen from other players via addon comms.
local UPDATECHECK_PREFIX_MAIN = "RSTATS_VER"
local UPDATECHECK_PREFIX_ACH  = "RSTATS_ACHV"

local function ParseTagVersion(v)
    if type(v) ~= "string" then return nil end
    v = v:gsub("%s+", "")

    -- Accept: v3.12 / v3.12-beta / 3.12
    local major, build, suffix = v:match("^v?(%d+)%.(%d+)(.*)$")
    if not major or not build then
        return nil
    end

    major = tonumber(major)
    build = tonumber(build)
    if not major or not build then
        return nil
    end

    local isBeta = false
    if suffix and suffix ~= "" then
        isBeta = (suffix:lower():find("beta", 1, true) ~= nil)
    end

    return major, build, isBeta
end

local function IsNewerVersion(theirV, myV)
    local tMaj, tBuild, tBeta = ParseTagVersion(theirV)
    local mMaj, mBuild, mBeta = ParseTagVersion(myV)
    if not tMaj or not mMaj then return false end

    if tMaj ~= mMaj then return tMaj > mMaj end
    if tBuild ~= mBuild then return tBuild > mBuild end

    -- Same numeric: release beats beta
    if mBeta and not tBeta then return true end
    return false
end

local function GetSendScope()
    if IsInGroup(LE_PARTY_CATEGORY_INSTANCE) then return "INSTANCE_CHAT" end
    if IsInRaid() then return "RAID" end
    if IsInGroup() then return "PARTY" end
    return nil
end

local function GetPlayerDB()
    -- Ensure the SavedVariables reference is wired up.
    if type(LoadData) == "function" then
        LoadData()
    end

    if not RSTATS or not RSTATS.Database then
        return nil
    end

    local key = GetPlayerKey()
    return RSTATS.Database[key]
end

function RSTATS:OpenSettings()
    if Settings and Settings.OpenToCategory and self and self.SettingsCategoryID then
        Settings.OpenToCategory(self.SettingsCategoryID)
    end
end

local function MaybeAnnounceVersion(db, enabledKey, lastSeenKey, addon, label)
    if not db or not db.settings then return end
    if not db.settings[enabledKey] then return end

    local current = C_AddOns.GetAddOnMetadata(addon, "Version")
    if not current or current == "" then
        return
    end

    local last = db.settings[lastSeenKey]
    if last == current then
        return
    end

    -- First install: just store the version silently.
    if last == nil then
        db.settings[lastSeenKey] = current
        return
    end

    RSPrint(string.format("%s updated: %s", label, current))
    db.settings[lastSeenKey] = current
end

EventUtil.ContinueOnAddOnLoaded("RatedStats", function()
    local db = GetPlayerDB()
    if not db then return end
    db.settings = db.settings or {}

    -- Main addon category
    local category = Settings.RegisterVerticalLayoutCategory(CATEGORY_NAME)
    -- Store the ID so the UI button can reliably open the correct page later
    RSTATS.SettingsCategoryID = category:GetID()
    do
        local setting = Settings.RegisterAddOnSetting(
            category,
            "RSTATS_MAIN_TELL_UPDATES",
            "mainTellUpdates",
            db.settings,
            Settings.VarType.Boolean,
            "Tell me of new updates",
            true
        )
        Settings.CreateCheckbox(category, setting, "Will announce when a newer version is seen in your group (and when you have updated).")
    end

    -- Tools: Export / Wipe (implemented as action-style checkboxes)
    -- These settings are NOT meant to stay checked. Clicking them performs an action then resets back to false.
    do
        db.settings.toolsExport = false
        local setting = Settings.RegisterAddOnSetting(
            category,
            "RSTATS_TOOLS_EXPORT",
            "toolsExport",
            db.settings,
            Settings.VarType.Boolean,
            "Export historical data",
            false
        )

        local guard = false
        setting:SetValueChangedCallback(function()
            if guard then return end
            if db.settings.toolsExport then
                if RSTATS and RSTATS.OpenExportWindow then
                    RSTATS:OpenExportWindow()
                end
                guard = true
                db.settings.toolsExport = false
                setting:SetValue(false)
                guard = false
            end
        end)

        Settings.CreateCheckbox(category, setting, "Opens an export window so you can copy your SavedVariables data out of the game.")
    end

    do
        db.settings.toolsWipe = false
        local setting = Settings.RegisterAddOnSetting(
            category,
            "RSTATS_TOOLS_WIPE",
            "toolsWipe",
            db.settings,
            Settings.VarType.Boolean,
            "Wipe database (this character)",
            false
        )

        local guard = false
        setting:SetValueChangedCallback(function()
            if guard then return end
            if db.settings.toolsWipe then
                if RSTATS and RSTATS.ConfirmWipeDatabase then
                    RSTATS:ConfirmWipeDatabase()
                end
                guard = true
                db.settings.toolsWipe = false
                setting:SetValue(false)
                guard = false
            end
        end)

        Settings.CreateCheckbox(category, setting, "Deletes all Rated Stats history for your current character.\n\nTo wipe all databases wipe from CurseForge or delete in the WTF Folder")
    end

    do
        local optsSummaryGraph = Settings.CreateControlTextContainer()
        optsSummaryGraph:Add(2, "Candlestick")
        optsSummaryGraph:Add(1, "Points")

        if type(db.settings.summaryGraphStyle) ~= "number" then
            db.settings.summaryGraphStyle = 2
        end

        local setting = Settings.RegisterAddOnSetting(
            category,
            "RSTATS_SUMMARY_GRAPH_STYLE",
            "summaryGraphStyle",
            db.settings,
            Settings.VarType.Number,
            "Summary graph style",
            2
        )
        Settings.CreateDropdown(category, setting, function() return optsSummaryGraph:GetData() end, nil)

        setting:SetValueChangedCallback(function()
            if RSTATS and RSTATS.Summary and RSTATS.Summary.frame and RSTATS.Summary.frame:IsShown() then
                RSTATS.Summary:Refresh()
            end
        end)
    end

    Settings.RegisterAddOnCategory(category)

    -- Achievements subcategory (only if the module is installed + enabled)
    local achievName = "Achievements"
    local achievAddon = "RatedStats_Achiev"

    if C_AddOns and C_AddOns.DoesAddOnExist and C_AddOns.DoesAddOnExist(achievAddon)
        and C_AddOns.GetAddOnEnableState and (C_AddOns.GetAddOnEnableState(achievAddon, nil) ~= 0) then

        local subcategory, layout = Settings.RegisterVerticalLayoutSubcategory(category, achievName)

        -- Dropdown option containers (must not be nil; Blizzard asserts if they are)
        local optsSS = Settings.CreateControlTextContainer()
        optsSS:Add(0, "None")
        optsSS:Add(1, "Self")
        optsSS:Add(4, "Say")
        optsSS:Add(5, "Yell")
        optsSS:Add(3, "Instance")

        local opts2v2 = Settings.CreateControlTextContainer()
        opts2v2:Add(0, "None")
        opts2v2:Add(1, "Self")
        opts2v2:Add(4, "Say")
        opts2v2:Add(5, "Yell")
        opts2v2:Add(2, "Party")

        local opts3v3 = Settings.CreateControlTextContainer()
        opts3v3:Add(0, "None")
        opts3v3:Add(1, "Self")
        opts3v3:Add(4, "Say")
        opts3v3:Add(5, "Yell")
        opts3v3:Add(2, "Party")

        local optsRBG = Settings.CreateControlTextContainer()
        optsRBG:Add(0, "None")
        optsRBG:Add(1, "Self")
        optsRBG:Add(4, "Say")
        optsRBG:Add(5, "Yell")
        optsRBG:Add(7, "Party")
        optsRBG:Add(6, "Raid")
        optsRBG:Add(3, "Instance")

        local optsRBGB = Settings.CreateControlTextContainer()
        optsRBGB:Add(0, "None")
        optsRBGB:Add(1, "Self")
        optsRBGB:Add(4, "Say")
        optsRBGB:Add(5, "Yell")
        optsRBGB:Add(3, "Instance")

        do
            local setting = Settings.RegisterAddOnSetting(
                subcategory,
                "RSTATS_ACHIEV_TELL_UPDATES",
                "achievTellUpdates",
                db.settings,
                Settings.VarType.Boolean,
                "Tell me of new updates",
                true
            )
            Settings.CreateCheckbox(subcategory, setting, "Will announce when a newer version is seen in your group (and when you have updated).")
        end

        do
            local setting = Settings.RegisterAddOnSetting(
                subcategory,
                "RSTATS_ACHIEV_ANNOUNCE_ON_QUEUE",
                "achievAnnounceOnQueue",
                db.settings,
                Settings.VarType.Boolean,
                "Announce on PvP queue",
                true
            )
            Settings.CreateCheckbox(subcategory, setting, "Will announce party/raid achievements when you all accept the PvP queue.")
        end

        if layout then
            -- Plain instructional text (HandyNotes style). Different client builds expose this initializer differently.
            local TextInit =
                _G.CreateSettingsListTextInitializer
                or (Settings and Settings.CreateSettingsListTextInitializer)

            local SubHeaderInit =
                _G.CreateSettingsListSubsectionHeaderInitializer
                or (Settings and Settings.CreateSettingsListSubsectionHeaderInitializer)

            if TextInit then
                layout:AddInitializer(TextInit("Options for Achievements Announcements"))
            elseif SubHeaderInit then
                -- Fallback: smaller than a full section header.
                layout:AddInitializer(SubHeaderInit("Options for Achievements Announcements"))
            elseif _G.CreateSettingsListSectionHeaderInitializer then
                -- Last resort (big header).
                layout:AddInitializer(CreateSettingsListSectionHeaderInitializer("Options for Achievements Announcements"))
            end
        end

        do
            local setting = Settings.RegisterAddOnSetting(
                subcategory,
                "RSTATS_ACHIEV_ANNOUNCE_SS",
                "achievAnnounceSS",
                db.settings,
                Settings.VarType.Number,
                "In SS announce to",
                3
            )
            Settings.CreateDropdown(subcategory, setting, function() return optsSS:GetData() end, nil)
        end

        do
            local setting = Settings.RegisterAddOnSetting(
                subcategory,
                "RSTATS_ACHIEV_ANNOUNCE_2V2",
                "achievAnnounce2v2",
                db.settings,
                Settings.VarType.Number,
                "In 2v2 announce to",
                2
            )
            Settings.CreateDropdown(subcategory, setting, function() return opts2v2:GetData() end, nil)
        end

        do
            local setting = Settings.RegisterAddOnSetting(
                subcategory,
                "RSTATS_ACHIEV_ANNOUNCE_3V3",
                "achievAnnounce3v3",
                db.settings,
                Settings.VarType.Number,
                "In 3v3 announce to",
                2
            )
            Settings.CreateDropdown(subcategory, setting, function() return opts3v3:GetData() end, nil)
        end

        do
            local setting = Settings.RegisterAddOnSetting(
                subcategory,
                "RSTATS_ACHIEV_ANNOUNCE_RBG",
                "achievAnnounceRBG",
                db.settings,
                Settings.VarType.Number,
                "In RBG announce to",
                1
            )
            Settings.CreateDropdown(subcategory, setting, function() return optsRBG:GetData() end, nil)
        end

        do
            local setting = Settings.RegisterAddOnSetting(
                subcategory,
                "RSTATS_ACHIEV_ANNOUNCE_RBGB",
                "achievAnnounceRBGB",
                db.settings,
                Settings.VarType.Number,
                "In RBGB announce to",
                1
            )
            Settings.CreateDropdown(subcategory, setting, function() return optsRBGB:GetData() end, nil)
        end
    end

    -- Battleground Enemies subcategory (only if the module is installed + enabled)
    local bgeName  = "BattleGround Enemies"
    local bgeAddon = "RatedStats_BattlegroundEnemies"

    if C_AddOns and C_AddOns.DoesAddOnExist and C_AddOns.DoesAddOnExist(bgeAddon)
        and C_AddOns.GetAddOnEnableState and (C_AddOns.GetAddOnEnableState(bgeAddon, nil) ~= 0) then

        local subcategory, layout = Settings.RegisterVerticalLayoutSubcategory(category, bgeName)

        -- Avoid running ApplySettings inside Settings callbacks and never run it in combat.
        -- If a change happens in combat, apply it right after combat ends.
        local bgeApplyPending = false

        -- Defer nameplate distance writes if user changes the slider during combat.
        local bgeDistanceApplyPending = false
        local bgeDistancePendingValue = nil

        local function RunBGENameplateDistanceApply()
            if not bgeDistanceApplyPending then return end
            if InCombatLockdown and InCombatLockdown() then return end
            bgeDistanceApplyPending = false

            local v = bgeDistancePendingValue
            bgeDistancePendingValue = nil
            if type(v) ~= "number" then return end

            -- Apply the CVar safely (out of combat only).
            if type(SetCVar) == "function" then
                pcall(SetCVar, "nameplateMaxDistance", tostring(v))
            elseif C_CVar and C_CVar.SetCVar then
                pcall(C_CVar.SetCVar, "nameplateMaxDistance", tostring(v))
            end
        end

        local function NotifyBGENameplateDistance()
            -- Only do anything when user changes the slider.
            -- Never write the CVar in combat; defer until combat ends.
            local v = db and db.settings and db.settings.bgeNameplateMaxDistance or nil
            if type(v) ~= "number" then return end

            if InCombatLockdown and InCombatLockdown() then
                bgeDistancePendingValue = v
                bgeDistanceApplyPending = true
                return
            end

            bgeDistancePendingValue = v
            bgeDistanceApplyPending = true
            RunBGENameplateDistanceApply()
        end

        local function RS_IsInInstancedPvP()
            -- BG-only: "active battlefield" is the correct gate for battleground instances.
            if C_PvP and C_PvP.IsActiveBattlefield and C_PvP.IsActiveBattlefield() then
                return true
            end

            -- Fallback: instance type "pvp" covers battleground instances.
            local inInstance, instanceType = IsInInstance()
            if inInstance and instanceType == "pvp" then
                return true
            end

            return false
        end

        local function RS_BGE_PreviewEnabled()
            -- Outside PvP, only apply if any preview mode is enabled.
            if not db or not db.settings then return false end
            if db.settings.bgeRatedPreview then return true end
            if db.settings.bge10Preview then return true end
            if db.settings.bge15Preview then return true end
            if db.settings.bgeLargePreview then return true end
            return false
        end

        local function ShouldApplyBGE()
            -- Apply in instanced PvP, or outside PvP only if any preview is enabled.
            return RS_IsInInstancedPvP() or RS_BGE_PreviewEnabled()
        end

        local function RunBGEApply()
            if not bgeApplyPending then return end
            if InCombatLockdown and InCombatLockdown() then return end
            bgeApplyPending = false
            -- BG-only safety: don't touch BGE runtime in PvE unless preview is enabled.
            local bge = _G.RSTATS_BGE
            if not bge then return end
            if not ShouldApplyBGE() and not bge.frame then
                return
            end
            local bge = _G.RSTATS_BGE
            if bge and type(bge.ApplySettings) == "function" then
                -- Allow ApplySettings to run even when preview is being turned OFF,
                -- as long as a preview frame already exists (so it can be hidden).
                if not ShouldApplyBGE() and not bge.frame then
                    return
                end

                -- Preview mode switching in PvE must not be "sticky".
                -- Force BGE to re-resolve which preview profile is active.
                bge._profilePrefix = nil

                if type(securecall) == "function" then
                    securecall(bge.ApplySettings, bge)
                else
                    bge:ApplySettings()
                end
            end
        end

        local function NotifyBGE()
			local bge = _G.RSTATS_BGE
			if not bge then
				bgeApplyPending = false
				return
			end
		
			-- IMPORTANT:
			-- If a preview frame is already visible, we MUST still run ApplySettings
			-- so toggling preview OFF can HIDE it.
			if not ShouldApplyBGE() and not bge.frame then
				bgeApplyPending = false
				return
			end
		
			-- Preview profile switching in PvE must not stay "sticky".
			-- Force BGE to re-resolve which preview profile is active.
			bge._profilePrefix = nil
            bgeApplyPending = true
            if C_Timer and C_Timer.After then
                C_Timer.After(0, RunBGEApply)
            else
                RunBGEApply()
            end
        end

        do
            local f = CreateFrame("Frame")
            f:RegisterEvent("PLAYER_REGEN_ENABLED")
            f:SetScript("OnEvent", function()
                -- If something queued during combat, apply immediately after combat ends (but still BG/preview gated).
                RunBGENameplateDistanceApply()
                RunBGEApply()
            end)
        end

        local TextInit =
            _G.CreateSettingsListTextInitializer
            or (Settings and Settings.CreateSettingsListTextInitializer)

        local SubHeaderInit =
            _G.CreateSettingsListSubsectionHeaderInitializer
            or (Settings and Settings.CreateSettingsListSubsectionHeaderInitializer)

        local SectionHeaderInit = _G.CreateSettingsListSectionHeaderInitializer

        if layout and TextInit then
            layout:AddInitializer(TextInit("Enemy frames built from NAME_PLATE_UNIT_* events. Only enemies with an active nameplate can be shown."))
        elseif layout and SubHeaderInit then
            layout:AddInitializer(SubHeaderInit("Battleground Enemies"))
        end

        -- ============================
        -- General
        -- ============================
        if layout then
            if SectionHeaderInit then
                layout:AddInitializer(SectionHeaderInit("General"))
            elseif SubHeaderInit then
                layout:AddInitializer(SubHeaderInit("General"))
            end
        end

        do
            local setting = Settings.RegisterAddOnSetting(
                subcategory,
                "RSTATS_BGE_ENABLED",
                "bgeEnabled",
                db.settings,
                Settings.VarType.Boolean,
                "Enable",
                true
            )
            setting:SetValueChangedCallback(function() NotifyBGE() end)
            Settings.CreateCheckbox(subcategory, setting, "Show enemy frames when in PvP instances.")
        end

        do
            local setting = Settings.RegisterAddOnSetting(
                subcategory,
                "RSTATS_BGE_LOCKED",
                "bgeLocked",
                db.settings,
                Settings.VarType.Boolean,
                "Lock frame",
                true
            )
            setting:SetValueChangedCallback(function() NotifyBGE() end)
            Settings.CreateCheckbox(subcategory, setting, "When unlocked, you can drag the frame to move it.")
        end

        do
            local optsLayout = Settings.CreateControlTextContainer()
            optsLayout:Add(1, "Single list")
            optsLayout:Add(2, "Grouped (sorted)")

            local setting = Settings.RegisterAddOnSetting(
                subcategory,
                "RSTATS_BGE_LAYOUT",
                "bgeLayout",
                db.settings,
                Settings.VarType.Number,
                "Layout",
                1
            )
            setting:SetValueChangedCallback(function() NotifyBGE() end)
            Settings.CreateDropdown(subcategory, setting, function() return optsLayout:GetData() end, nil)
        end

        do
            local setting = Settings.RegisterAddOnSetting(
                subcategory,
                "RSTATS_BGE_SHOW_ACHIEV_ICON",
                "bgeShowAchievIcon",
                db.settings,
                Settings.VarType.Boolean,
                "Show achievement icon",
                false
            )
            setting:SetValueChangedCallback(function() NotifyBGE() end)
            Settings.CreateCheckbox(subcategory, setting, "Shows an icon if Rated Stats - Achievements exposes an icon lookup API.")
        end

        do
            local setting = Settings.RegisterAddOnSetting(
                subcategory,
                "RSTATS_BGE_SHOW_POWER",
                "bgeShowPower",
                db.settings,
                Settings.VarType.Boolean,
                "Show power bar",
                true
            )
            setting:SetValueChangedCallback(function() NotifyBGE() end)
            Settings.CreateCheckbox(subcategory, setting, "Shows mana/energy/rage/etc (when available).")
        end

        do
            local opts = Settings.CreateControlTextContainer()
            opts:Add(1, "Current")
            opts:Add(2, "Current/Total")
            opts:Add(3, "%")

            local setting = Settings.RegisterAddOnSetting(
                subcategory,
                "RSTATS_BGE_HEALTH_TEXT",
                "bgeHealthTextMode",
                db.settings,
                Settings.VarType.Number,
                "Health text",
                2
            )
            setting:SetValueChangedCallback(function() NotifyBGE() end)
            Settings.CreateDropdown(subcategory, setting, function() return opts:GetData() end, nil)
        end

        -- Current Distance (nameplateMaxDistance)
        --
        -- IMPORTANT: do NOT register a live CVar setting into Blizzard Settings.
        -- We store the value in our DB and apply the CVar ourselves (out of combat only).
        do
            local tooltip = "Range may seem far for enemies, consider your own preference."

            if db and db.settings and type(db.settings.bgeNameplateMaxDistance) ~= "number" then
                local cur = nil
                if type(GetCVar) == "function" then
                    cur = tonumber(GetCVar("nameplateMaxDistance"))
                end
                db.settings.bgeNameplateMaxDistance = cur or 60
            end

            local setting = Settings.RegisterAddOnSetting(
                subcategory,
                "RSTATS_BGE_NAMEPLATE_MAX_DISTANCE",
                "bgeNameplateMaxDistance",
                db.settings,
                Settings.VarType.Number,
                "Current Distance",
                db.settings.bgeNameplateMaxDistance or 60
            )

            setting:SetValueChangedCallback(function()
                NotifyBGENameplateDistance()
            end)

            local options = Settings.CreateSliderOptions(0, 60, 1)
            if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
            end
            Settings.CreateSlider(subcategory, setting, options, tooltip)
        end

        -- ============================
        -- Layout profiles
        -- ============================

        -- This is not displayed as a control; it is driven by our tab buttons below.
        local tabSetting = Settings.RegisterAddOnSetting(
            subcategory,
            "RSTATS_BGE_PROFILE_TAB",
            "bgeProfileTab",
            db.settings,
            Settings.VarType.Number,
            "Profile tab",
            1
        )

        local function GetCurrentTab()
            local v = (db.settings and db.settings.bgeProfileTab) or nil
            if type(v) ~= "number" then
                v = 1
            end
            if v < 1 or v > 4 then
                v = 1
            end
            return v
        end

        local function AddTabPredicate(initializer, tabIndex)
            if not initializer then return end
            initializer:AddShownPredicate(function()
                return GetCurrentTab() == tabIndex
            end)
        end

        -- Keep preview toggles in sync when switching profiles.
        local bgeSwitchingPreview = false
        local bgeRatedPreviewSetting
        local bge10PreviewSetting
        local bge15PreviewSetting
        local bgeLargePreviewSetting

        local function AnyPreviewEnabled()
            if not db or not db.settings then return false end
            local s = db.settings
            return (s.bgeRatedPreview or s.bge10Preview or s.bge15Preview or s.bgeLargePreview) and true or false
        end

        local function SetPreviewExclusiveForTab(tab)
            if not db or not db.settings then return end
            bgeSwitchingPreview = true

            local wantRated = (tab == 1)
            local want10    = (tab == 2)
            local want15    = (tab == 3)
            local wantLarge = (tab == 4)

            -- Use Setting:SetValue so Blizzard Settings UI checkboxes update immediately.
            if bgeRatedPreviewSetting and bgeRatedPreviewSetting.SetValue then
                bgeRatedPreviewSetting:SetValue(wantRated)
            else
                db.settings.bgeRatedPreview = wantRated
            end
            if bge10PreviewSetting and bge10PreviewSetting.SetValue then
                bge10PreviewSetting:SetValue(want10)
            else
                db.settings.bge10Preview = want10
            end
            if bge15PreviewSetting and bge15PreviewSetting.SetValue then
                bge15PreviewSetting:SetValue(want15)
            else
                db.settings.bge15Preview = want15
            end
            if bgeLargePreviewSetting and bgeLargePreviewSetting.SetValue then
                bgeLargePreviewSetting:SetValue(wantLarge)
            else
                db.settings.bgeLargePreview = wantLarge
            end

            bgeSwitchingPreview = false
        end

        -- Layout profile selector (safe: no overriding Blizzard initializer methods)
        do
            if layout then
                if SectionHeaderInit then
                    layout:AddInitializer(SectionHeaderInit("Layout Profiles"))
                elseif SubHeaderInit then
                    layout:AddInitializer(SubHeaderInit("Layout Profiles"))
                end
            end

            local optsTab = Settings.CreateControlTextContainer()
            optsTab:Add(1, "Rated (8v8)")
            optsTab:Add(2, "10v10")
            optsTab:Add(3, "15v15")
            optsTab:Add(4, ">15")

            tabSetting:SetValueChangedCallback(function()
                -- Force the Settings list to rebuild shown predicates when the tab changes.
                -- Do NOT do anything in combat.
                if InCombatLockdown and InCombatLockdown() then return end

                -- If preview is currently on for any profile, transfer it to the newly selected profile
                -- and turn the previous profile off.
                if AnyPreviewEnabled() then
                    SetPreviewExclusiveForTab(GetCurrentTab())
                    NotifyBGE()
                end

                -- If any preview is on, transfer it to the selected profile (exclusive).
                if AnyPreviewEnabled() then
                    SetPreviewExclusiveForTab(GetCurrentTab())
                    NotifyBGE()
                end

                if SettingsInbound and SettingsInbound.RepairDisplay then
                    SettingsInbound.RepairDisplay()
                end
            end)

            Settings.CreateDropdown(subcategory, tabSetting, function() return optsTab:GetData() end, "Choose which profile settings are shown below.")
        end

        -- ----------------------------
        -- Rated (8v8)
        -- ----------------------------
        do
            local hdr
            if SubHeaderInit and layout then

                hdr = SubHeaderInit("Rated (8v8)")

                AddTabPredicate(hdr, 1)

                layout:AddInitializer(hdr)

            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_RATED_PREVIEW",
                    "bgeRatedPreview",
                    db.settings,
                    Settings.VarType.Boolean,
                    "Preview outside PvP",
                    false
                )
                bgeRatedPreviewSetting = setting
                setting:SetValueChangedCallback(function()
                    if bgeSwitchingPreview then return end
                    if db and db.settings and db.settings.bgeRatedPreview then
                        db.settings.bge10Preview = false
                        db.settings.bge15Preview = false
                        db.settings.bgeLargePreview = false
                        db.settings.bgeProfileTab = 1
                    end
                    NotifyBGE()
                end)
                local init = Settings.CreateCheckbox(subcategory, setting, "Shows the frame out of PvP so you can tune size/position for Rated (8v8).")
                AddTabPredicate(init, 1)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_RATED_PREVIEW_COUNT",
                    "bgeRatedPreviewCount",
                    db.settings,
                    Settings.VarType.Number,
                    "Preview rows",
                    8
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(1, 40, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "How many blank rows to show in preview mode (Rated 8v8).")
                AddTabPredicate(init, 1)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_RATED_COLUMNS",
                    "bgeRatedColumns",
                    db.settings,
                    Settings.VarType.Number,
                    "Columns",
                    2
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(1, 8, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "Number of columns for Rated (8v8).")
                AddTabPredicate(init, 1)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_RATED_ROWS_PER_COL",
                    "bgeRatedRowsPerCol",
                    db.settings,
                    Settings.VarType.Number,
                    "Rows per column",
                    5
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(1, 40, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "Wrap after this many rows (Rated 8v8).")
                AddTabPredicate(init, 1)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_RATED_COL_GAP",
                    "bgeRatedColGap",
                    db.settings,
                    Settings.VarType.Number,
                    "Column gap",
                    0
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(0, 30, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "Space between columns (Rated 8v8).")
                AddTabPredicate(init, 1)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_RATED_ROW_WIDTH",
                    "bgeRatedRowWidth",
                    db.settings,
                    Settings.VarType.Number,
                    "Row width",
                    100
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(50, 520, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "Width of each row (Rated 8v8).")
                AddTabPredicate(init, 1)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_RATED_ROW_HEIGHT",
                    "bgeRatedRowHeight",
                    db.settings,
                    Settings.VarType.Number,
                    "Row height",
                    60
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(15, 140, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "Height of each row (Rated 8v8).")
                AddTabPredicate(init, 1)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_RATED_ROW_GAP",
                    "bgeRatedRowGap",
                    db.settings,
                    Settings.VarType.Number,
                    "Row gap",
                    0
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(0, 10, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "Space between rows (Rated 8v8).")
                AddTabPredicate(init, 1)
            end
        end

        -- ----------------------------
        -- 10v10
        -- ----------------------------
        do
            local hdr
            if SubHeaderInit and layout then

                hdr = SubHeaderInit("10v10")

                AddTabPredicate(hdr, 2)

                layout:AddInitializer(hdr)

            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_10_PREVIEW",
                    "bge10Preview",
                    db.settings,
                    Settings.VarType.Boolean,
                    "Preview outside PvP",
                    false
                )
                bge10PreviewSetting = setting
                setting:SetValueChangedCallback(function()
                    if bgeSwitchingPreview then return end
                    if db and db.settings and db.settings.bge10Preview then
                        db.settings.bgeRatedPreview = false
                        db.settings.bge15Preview = false
                        db.settings.bgeLargePreview = false
                        db.settings.bgeProfileTab = 2
                    end
                    NotifyBGE()
                end)
                local init = Settings.CreateCheckbox(subcategory, setting, "Shows the frame out of PvP so you can tune size/position for 10v10.")
                AddTabPredicate(init, 2)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_10_PREVIEW_COUNT",
                    "bge10PreviewCount",
                    db.settings,
                    Settings.VarType.Number,
                    "Preview rows",
                    10
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(1, 40, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "How many blank rows to show in preview mode (10v10).")
                AddTabPredicate(init, 2)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_10_COLUMNS",
                    "bge10Columns",
                    db.settings,
                    Settings.VarType.Number,
                    "Columns",
                    2
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(1, 8, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "Number of columns for 10v10.")
                AddTabPredicate(init, 2)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_10_ROWS_PER_COL",
                    "bge10RowsPerCol",
                    db.settings,
                    Settings.VarType.Number,
                    "Rows per column",
                    5
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(1, 40, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "Wrap after this many rows (10v10).")
                AddTabPredicate(init, 2)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_10_COL_GAP",
                    "bge10ColGap",
                    db.settings,
                    Settings.VarType.Number,
                    "Column gap",
                    0
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(0, 30, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "Space between columns (10v10).")
                AddTabPredicate(init, 2)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_10_ROW_WIDTH",
                    "bge10RowWidth",
                    db.settings,
                    Settings.VarType.Number,
                    "Row width",
                    100
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(50, 520, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "Width of each row (10v10).")
                AddTabPredicate(init, 2)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_10_ROW_HEIGHT",
                    "bge10RowHeight",
                    db.settings,
                    Settings.VarType.Number,
                    "Row height",
                    60
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(15, 140, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "Height of each row (10v10).")
                AddTabPredicate(init, 2)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_10_ROW_GAP",
                    "bge10RowGap",
                    db.settings,
                    Settings.VarType.Number,
                    "Row gap",
                    0
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(0, 10, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "Space between rows (10v10).")
                AddTabPredicate(init, 2)
            end
        end

        -- ----------------------------
        -- 15v15
        -- ----------------------------
        do
            local hdr
            if SubHeaderInit and layout then

                hdr = SubHeaderInit("15v15")

                AddTabPredicate(hdr, 3)

                layout:AddInitializer(hdr)

            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_15_PREVIEW",
                    "bge15Preview",
                    db.settings,
                    Settings.VarType.Boolean,
                    "Preview outside PvP",
                    false
                )
                bge15PreviewSetting = setting
                setting:SetValueChangedCallback(function()
                    if bgeSwitchingPreview then return end
                    if db and db.settings and db.settings.bge15Preview then
                        db.settings.bgeRatedPreview = false
                        db.settings.bge10Preview = false
                        db.settings.bgeLargePreview = false
                        db.settings.bgeProfileTab = 3
                    end
                    NotifyBGE()
                end)
                local init = Settings.CreateCheckbox(subcategory, setting, "Shows the frame out of PvP so you can tune size/position for 15v15.")
                AddTabPredicate(init, 3)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_15_PREVIEW_COUNT",
                    "bge15PreviewCount",
                    db.settings,
                    Settings.VarType.Number,
                    "Preview rows",
                    15
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(1, 40, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "How many blank rows to show in preview mode (15v15).")
                AddTabPredicate(init, 3)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_15_COLUMNS",
                    "bge15Columns",
                    db.settings,
                    Settings.VarType.Number,
                    "Columns",
                    3
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(1, 8, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "Number of columns for 15v15.")
                AddTabPredicate(init, 3)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_15_ROWS_PER_COL",
                    "bge15RowsPerCol",
                    db.settings,
                    Settings.VarType.Number,
                    "Rows per column",
                    5
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(1, 40, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "Wrap after this many rows (15v15).")
                AddTabPredicate(init, 3)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_15_COL_GAP",
                    "bge15ColGap",
                    db.settings,
                    Settings.VarType.Number,
                    "Column gap",
                    0
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(0, 30, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "Space between columns (15v15).")
                AddTabPredicate(init, 3)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_15_ROW_WIDTH",
                    "bge15RowWidth",
                    db.settings,
                    Settings.VarType.Number,
                    "Row width",
                    100
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(50, 520, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "Width of each row (15v15).")
                AddTabPredicate(init, 3)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_15_ROW_HEIGHT",
                    "bge15RowHeight",
                    db.settings,
                    Settings.VarType.Number,
                    "Row height",
                    60
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(15, 140, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "Height of each row (15v15).")
                AddTabPredicate(init, 3)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_15_ROW_GAP",
                    "bge15RowGap",
                    db.settings,
                    Settings.VarType.Number,
                    "Row gap",
                    0
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(0, 10, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "Space between rows (15v15).")
                AddTabPredicate(init, 3)
            end
        end

        -- ----------------------------
        -- >15v15
        -- ----------------------------
        do
            local hdr
            if SubHeaderInit and layout then

                hdr = SubHeaderInit(">15v15")

                AddTabPredicate(hdr, 4)

                layout:AddInitializer(hdr)

            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_LARGE_PREVIEW",
                    "bgeLargePreview",
                    db.settings,
                    Settings.VarType.Boolean,
                    "Preview outside PvP",
                    false
                )
                bgeLargePreviewSetting = setting
                setting:SetValueChangedCallback(function()
                    if bgeSwitchingPreview then return end
                    if db and db.settings and db.settings.bgeLargePreview then
                        db.settings.bgeRatedPreview = false
                        db.settings.bge10Preview = false
                        db.settings.bge15Preview = false
                        db.settings.bgeProfileTab = 4
                    end
                    NotifyBGE()
                end)
                local init = Settings.CreateCheckbox(subcategory, setting, "Shows the frame out of PvP so you can tune size/position for >15v15.")
                AddTabPredicate(init, 4)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_LARGE_PREVIEW_COUNT",
                    "bgeLargePreviewCount",
                    db.settings,
                    Settings.VarType.Number,
                    "Preview rows",
                    40
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(1, 40, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "How many blank rows to show in preview mode (>15v15).")
                AddTabPredicate(init, 4)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_LARGE_COLUMNS",
                    "bgeLargeColumns",
                    db.settings,
                    Settings.VarType.Number,
                    "Columns",
                    4
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(1, 8, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "Number of columns for >15v15.")
                AddTabPredicate(init, 4)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_LARGE_ROWS_PER_COL",
                    "bgeLargeRowsPerCol",
                    db.settings,
                    Settings.VarType.Number,
                    "Rows per column",
                    10
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(1, 40, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "Wrap after this many rows (>15v15).")
                AddTabPredicate(init, 4)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_LARGE_COL_GAP",
                    "bgeLargeColGap",
                    db.settings,
                    Settings.VarType.Number,
                    "Column gap",
                    0
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(0, 30, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "Space between columns (>15v15).")
                AddTabPredicate(init, 4)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_LARGE_ROW_WIDTH",
                    "bgeLargeRowWidth",
                    db.settings,
                    Settings.VarType.Number,
                    "Row width",
                    100
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(50, 520, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "Width of each row (>15v15).")
                AddTabPredicate(init, 4)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_LARGE_ROW_HEIGHT",
                    "bgeLargeRowHeight",
                    db.settings,
                    Settings.VarType.Number,
                    "Row height",
                    60
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(15, 140, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "Height of each row (>15v15).")
                AddTabPredicate(init, 4)
            end

            do
                local setting = Settings.RegisterAddOnSetting(
                    subcategory,
                    "RSTATS_BGE_LARGE_ROW_GAP",
                    "bgeLargeRowGap",
                    db.settings,
                    Settings.VarType.Number,
                    "Row gap",
                    0
                )
                setting:SetValueChangedCallback(function() NotifyBGE() end)
                local options = Settings.CreateSliderOptions(0, 10, 1)
                if MinimalSliderWithSteppersMixin and MinimalSliderWithSteppersMixin.Label and options.SetLabelFormatter then
                    options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
                end
                local init = Settings.CreateSlider(subcategory, setting, options, "Space between rows (>15v15).")
                AddTabPredicate(init, 4)
            end
        end
    end

    -- Login announcements (main + Achievements module)
    local login = CreateFrame("Frame")
    login:RegisterEvent("PLAYER_LOGIN")
    login:SetScript("OnEvent", function()
        local db2 = GetPlayerDB()
        if not db2 then return end
        db2.settings = db2.settings or {}

        MaybeAnnounceVersion(db2, "mainTellUpdates", "mainLastSeenVersion", "RatedStats", "Rated Stats")

        -- Only announce Achievements updates if the module is enabled.
        if C_AddOns.GetAddOnEnableState("RatedStats_Achiev", nil) ~= 0 then
            MaybeAnnounceVersion(db2, "achievTellUpdates", "achievLastSeenVersion", "RatedStats_Achiev", "Rated Stats - Achievements")
        end
    end)

    -- Comms-based "update available" checker
    local ver = CreateFrame("Frame")
    ver:RegisterEvent("CHAT_MSG_ADDON")
    ver:RegisterEvent("GROUP_ROSTER_UPDATE")
    ver:RegisterEvent("PLAYER_ENTERING_WORLD")

    local announcedMainFor = nil
    local announcedAchFor  = nil

    local function MaybeSendMyVersions(reason)
        if not (C_ChatInfo and C_ChatInfo.SendAddonMessage) then return end

        local db3 = GetPlayerDB()
        if not db3 then return end
        db3.settings = db3.settings or {}

        local scope = GetSendScope()
        if not scope then return end

        local myMainVer = C_AddOns.GetAddOnMetadata("RatedStats", "Version") or ""
        local myAchVer  = ""
        if C_AddOns.GetAddOnEnableState("RatedStats_Achiev", nil) ~= 0 then
            myAchVer = C_AddOns.GetAddOnMetadata("RatedStats_Achiev", "Version") or ""
        end

        -- Persisted "send once until state changes" guards
        local newestMain = db3.settings.mainNewestSeenVersion or ""
        local newestAch  = db3.settings.achievNewestSeenVersion or ""

        local sentMainVer   = db3.settings.mainLastBroadcastVersion or ""
        local sentMainSeen  = db3.settings.mainLastBroadcastNewestSeen or ""
        local sentAchVer    = db3.settings.achievLastBroadcastVersion or ""
        local sentAchSeen   = db3.settings.achievLastBroadcastNewestSeen or ""

        -- MAIN
        if myMainVer ~= "" and ParseTagVersion(myMainVer) then
            -- Send once if:
            --  - we haven't sent this installed version yet, OR
            --  - the "newest seen" state has advanced since the last time we sent
            if (sentMainVer ~= myMainVer) or (sentMainSeen ~= newestMain) then
                C_ChatInfo.SendAddonMessage(UPDATECHECK_PREFIX_MAIN, myMainVer, scope)
                db3.settings.mainLastBroadcastVersion = myMainVer
                db3.settings.mainLastBroadcastNewestSeen = newestMain
            end
        end

        -- ACHIEV
        if myAchVer ~= "" and ParseTagVersion(myAchVer) then
            if (sentAchVer ~= myAchVer) or (sentAchSeen ~= newestAch) then
                C_ChatInfo.SendAddonMessage(UPDATECHECK_PREFIX_ACH, myAchVer, scope)
                db3.settings.achievLastBroadcastVersion = myAchVer
                db3.settings.achievLastBroadcastNewestSeen = newestAch
            end
        end
    end

    local function SendMyVersions()
        if not (C_ChatInfo and C_ChatInfo.SendAddonMessage) then return end

        local scope = GetSendScope()
        if not scope then return end

        local now = GetTime()
        if (now - lastSendAt) < 15 then return end
        lastSendAt = now

        local myMainVer = C_AddOns.GetAddOnMetadata("RatedStats", "Version") or ""
        if myMainVer ~= "" then
            C_ChatInfo.SendAddonMessage(UPDATECHECK_PREFIX_MAIN, myMainVer, scope)
        end

        if C_AddOns.GetAddOnEnableState("RatedStats_Achiev", nil) ~= 0 then
            local myAchVer = C_AddOns.GetAddOnMetadata("RatedStats_Achiev", "Version") or ""
            if myAchVer ~= "" then
                C_ChatInfo.SendAddonMessage(UPDATECHECK_PREFIX_ACH, myAchVer, scope)
            end
        end
    end

    local function HandleIncoming(prefix, msg, sender)
        local db3 = GetPlayerDB()
        if not db3 then return end
        db3.settings = db3.settings or {}

        if prefix == UPDATECHECK_PREFIX_MAIN then
            if not db3.settings.mainTellUpdates then return end

            local myMainVer = C_AddOns.GetAddOnMetadata("RatedStats", "Version") or ""
            if not ParseTagVersion(myMainVer) then return end
            if not ParseTagVersion(msg) then return end

            if not IsNewerVersion(msg, myMainVer) then return end

            local stored = db3.settings.mainNewestSeenVersion
            if stored and ParseTagVersion(stored) and not IsNewerVersion(msg, stored) then
                return
            end
            db3.settings.mainNewestSeenVersion = msg

            if announcedMainFor ~= msg then
                RSPrint(string.format("Rated Stats update available: you have %s, seen %s from %s.", myMainVer, msg, sender or "someone"))
                announcedMainFor = msg
            end
            MaybeSendMyVersions("main_newer_seen")

        elseif prefix == UPDATECHECK_PREFIX_ACH then
            if C_AddOns.GetAddOnEnableState("RatedStats_Achiev", nil) == 0 then return end
            if not db3.settings.achievTellUpdates then return end

            local myAchVer = C_AddOns.GetAddOnMetadata("RatedStats_Achiev", "Version") or ""
            if not ParseTagVersion(myAchVer) then return end
            if not ParseTagVersion(msg) then return end

            if not IsNewerVersion(msg, myAchVer) then return end

            local stored = db3.settings.achievNewestSeenVersion
            if stored and ParseTagVersion(stored) and not IsNewerVersion(msg, stored) then
                return
            end
            db3.settings.achievNewestSeenVersion = msg

            if announcedAchFor ~= msg then
                RSPrint(string.format("Rated Stats - Achievements update available: you have %s, seen %s from %s.", myAchVer, msg, sender or "someone"))
                announcedAchFor = msg
            end
            MaybeSendMyVersions("ach_newer_seen")
        end
    end

    ver:SetScript("OnEvent", function(_, event, prefix, msg, _, sender)
        if event == "CHAT_MSG_ADDON" then
            if prefix ~= UPDATECHECK_PREFIX_MAIN and prefix ~= UPDATECHECK_PREFIX_ACH then return end
            if type(msg) ~= "string" or msg == "" then return end
            HandleIncoming(prefix, msg, sender)
            return
        end

        -- group/zone changes: broadcast ONCE (persisted across reloads) unless state changed
        MaybeSendMyVersions(event)
    end)

    if C_ChatInfo and C_ChatInfo.RegisterAddonMessagePrefix then
        C_ChatInfo.RegisterAddonMessagePrefix(UPDATECHECK_PREFIX_MAIN)
        C_ChatInfo.RegisterAddonMessagePrefix(UPDATECHECK_PREFIX_ACH)
    end

    if C_Timer and C_Timer.After then
        C_Timer.After(2, function() MaybeSendMyVersions("initial") end)
    else
        MaybeSendMyVersions("initial")
    end
end)
