-- Portal Authority — options/shared_layout.lua
-- Shared panel hydration, first-open settle, layout, and scrollable container helpers.
--
-- Depends on: PortalAuthority.OptionsInternal shared helpers
-- Exposes:    PortalAuthority.OptionsInternal layout helpers
-- DB keys:    none direct
-- Events:     none
-- Search key: none

PortalAuthority = PortalAuthority or {}

if PortalAuthority.IsSettingsBaselineGateEnabled and PortalAuthority:IsSettingsBaselineGateEnabled() then
    return
end

if not Settings or not Settings.RegisterCanvasLayoutCategory then
    return
end

PortalAuthority.OptionsInternal = PortalAuthority.OptionsInternal or {}
local OptionsInternal = PortalAuthority.OptionsInternal

local settingsSearchRuntime = OptionsInternal.SearchRuntime
local trim = OptionsInternal.trim
local rgba255 = OptionsInternal.rgba255
local unpackColor = OptionsInternal.unpackColor
local applyGlobalFontToObject = OptionsInternal.applyGlobalFontToObject
local getPortalMedia = OptionsInternal.getPortalMedia
local normalizeSoundChoiceValue = OptionsInternal.normalizeSoundChoiceValue
local compareChoiceLabels = OptionsInternal.compareChoiceLabels
local normalizeChoicePathKey = OptionsInternal.normalizeChoicePathKey
local resolveFontChoicePath = OptionsInternal.resolveFontChoicePath
local getSoundChoiceValueKey = OptionsInternal.getSoundChoiceValueKey
local buildFallbackSoundChoices = OptionsInternal.buildFallbackSoundChoices
local getSummonSoundChoices = OptionsInternal.getSummonSoundChoices
local getAvailableFontChoices = OptionsInternal.getAvailableFontChoices
local buildSharedFontPickerChoices = OptionsInternal.buildSharedFontPickerChoices
local SUMMON_SOUND_CHANNEL_CHOICES = OptionsInternal.SUMMON_SOUND_CHANNEL_CHOICES
local debugSettingsLog = OptionsInternal.debugSettingsLog or function() end
local FLAT_SETTINGS_BACKDROP = OptionsInternal.FLAT_SETTINGS_BACKDROP
local SETTINGS_WINDOW_HOSTED_TOP_RHYTHM_NUDGE = OptionsInternal.SETTINGS_WINDOW_HOSTED_TOP_RHYTHM_NUDGE
local SETTINGS_WINDOW_SCROLLBAR_WIDTH = OptionsInternal.SETTINGS_WINDOW_SCROLLBAR_WIDTH
local SETTINGS_WINDOW_SCROLLBAR_GAP = OptionsInternal.SETTINGS_WINDOW_SCROLLBAR_GAP
local SETTINGS_WINDOW_SCROLLBAR_MIN_THUMB_HEIGHT = OptionsInternal.SETTINGS_WINDOW_SCROLLBAR_MIN_THUMB_HEIGHT
local SETTINGS_WINDOW_HOST_COLORS = OptionsInternal.SETTINGS_WINDOW_HOST_COLORS
local SETTINGS_WINDOW_SCROLLBAR_TRACK = OptionsInternal.SETTINGS_WINDOW_SCROLLBAR_TRACK
local SETTINGS_WINDOW_SCROLLBAR_THUMB = OptionsInternal.SETTINGS_WINDOW_SCROLLBAR_THUMB
local SETTINGS_WINDOW_SCROLLBAR_THUMB_HOVER = OptionsInternal.SETTINGS_WINDOW_SCROLLBAR_THUMB_HOVER
local frameIsAlive = OptionsInternal.frameIsAlive
local isDescendedFromFrame = OptionsInternal.isDescendedFromFrame
local markSettingsWindowVisualOptIn = OptionsInternal.markSettingsWindowVisualOptIn
local isUsingCustomSettingsWindowVisualStyle = OptionsInternal.isUsingCustomSettingsWindowVisualStyle
local registerSettingsWindowVisualRefresh = OptionsInternal.registerSettingsWindowVisualRefresh
local setTextureSolidColor = OptionsInternal.setTextureSolidColor
local hideDefaultScrollBarPart = OptionsInternal.hideDefaultScrollBarPart
local getTemplateScrollBar = OptionsInternal.getTemplateScrollBar
local hideDefaultScrollBarChrome = OptionsInternal.hideDefaultScrollBarChrome
local refreshCustomSettingsScrollBarThumb = OptionsInternal.refreshCustomSettingsScrollBarThumb
local settingsScrollRuntime = PortalAuthority._settingsScrollRuntime or OptionsInternal.settingsScrollRuntime
local createStyledFontString = OptionsInternal.createStyledFontString
local createTopRightHeader = OptionsInternal.createTopRightHeader
local PA_SafeBool = OptionsInternal.PA_SafeBool
local getClassHex = OptionsInternal.getClassHex
local createCheckbox = OptionsInternal.createCheckbox
local createActionButton = OptionsInternal.createActionButton
local styleSecondaryActionButtonShared = OptionsInternal.styleSecondaryActionButtonShared
local createNumeric = OptionsInternal.createNumeric
local createDropdown = OptionsInternal.createDropdown
local createDynamicDropdown = OptionsInternal.createDynamicDropdown
local createPremiumSegmentedChoice = OptionsInternal.createPremiumSegmentedChoice
local CreatePicker = OptionsInternal.CreatePicker
local CreateStepperControl = OptionsInternal.CreateStepperControl
local createCanonicalDockFontSizeStepperControl = OptionsInternal.CreateStepperControl or OptionsInternal.createCanonicalDockFontSizeStepperControl
local createTextPlacement = OptionsInternal.createTextPlacement
local setSettingsControlEnabled = OptionsInternal.setSettingsControlEnabled
local setSettingsTextEnabled = OptionsInternal.setSettingsTextEnabled
local createScrollableChild = OptionsInternal.createScrollableChild
local updateScrollBehavior = OptionsInternal.updateScrollBehavior
local withPanelHydration = OptionsInternal.withPanelHydration
local panelHasSizedViewport = OptionsInternal.panelHasSizedViewport
local handleSettingsFirstOpenSizeReady = OptionsInternal.handleSettingsFirstOpenSizeReady
local handleSettingsFirstOpenPanelShow = OptionsInternal.handleSettingsFirstOpenPanelShow
local handleSettingsFirstOpenPanelHide = OptionsInternal.handleSettingsFirstOpenPanelHide
local shouldSuppressSettingsFirstOpenExternalRefresh = OptionsInternal.shouldSuppressSettingsFirstOpenExternalRefresh
local isPanelUsingCustomSettingsWindowHost = OptionsInternal.isPanelUsingCustomSettingsWindowHost
local refreshPanel = OptionsInternal.refreshPanel
local createScrollableTextArea = OptionsInternal.createScrollableTextArea
local createSettingsSectionDescriptor = OptionsInternal.createSettingsSectionDescriptor
local layoutSettingsSectionDescriptor = OptionsInternal.layoutSettingsSectionDescriptor
local IsPointerInsideActivePickerSafeZone = OptionsInternal.IsPointerInsideActivePickerSafeZone
local CloseActivePortalPicker = OptionsInternal.CloseActivePortalPicker
local settingsFirstOpenHost = OptionsInternal.settingsFirstOpenHost

local function createScrollableChild(panel)
    local scrollFrame = CreateFrame("ScrollFrame", nil, panel, "UIPanelScrollFrameTemplate")
    scrollFrame:SetPoint("TOPLEFT", panel, "TOPLEFT", 12, -12)
    scrollFrame:SetPoint("TOPRIGHT", panel, "TOPRIGHT", -30, -12)
    scrollFrame:SetPoint("BOTTOMLEFT", panel, "BOTTOMLEFT", 12, 14)
    scrollFrame:SetPoint("BOTTOMRIGHT", panel, "BOTTOMRIGHT", -30, 14)

    local scrollChild = CreateFrame("Frame", nil, scrollFrame)
    scrollChild:SetPoint("TOPLEFT")
    scrollChild:SetPoint("TOPRIGHT")
    scrollChild:SetHeight(1)
    scrollFrame:SetScrollChild(scrollChild)

    markSettingsWindowVisualOptIn(panel)
    markSettingsWindowVisualOptIn(scrollFrame)
    markSettingsWindowVisualOptIn(scrollChild)
    panel._paSettingsScrollFrame = scrollFrame
    panel._paSettingsScrollChild = scrollChild
    scrollFrame._paCloseFloatingControlsOnUserScroll = true
    scrollFrame._paCloseFloatingControlsPanel = panel
    scrollFrame._paOwnerPanel = panel
    if panel._paSearchSectionKey then
        panel._paSearchScrollFrame = scrollFrame
        panel._paSearchScrollChild = scrollChild
        scrollFrame._paSearchPanel = panel
        scrollChild._paSearchPanel = panel
        panel._paSearchRegistryDirty = true
    end
    registerSettingsWindowVisualRefresh(scrollFrame, function(selfScroll)
        local child = selfScroll.GetScrollChild and selfScroll:GetScrollChild() or scrollChild
        local viewportHeight = tonumber(selfScroll:GetHeight()) or 0
        local contentHeight = child and (tonumber(child:GetHeight()) or 0) or 0
        settingsScrollRuntime.sync(selfScroll, child, viewportHeight, contentHeight)
    end)
    settingsScrollRuntime.sync(scrollFrame, scrollChild, scrollFrame:GetHeight() or 0, scrollChild:GetHeight() or 0)

    return scrollFrame, scrollChild
end

local function updateScrollBehavior(scrollFrame, scrollChild)
    if not scrollFrame or not scrollChild then
        return
    end

    if scrollFrame.UpdateScrollChildRect then
        scrollFrame:UpdateScrollChildRect()
    end

    local viewportHeight = scrollFrame:GetHeight() or 0
    local contentHeight = scrollChild:GetHeight() or 0
    local needsScroll = contentHeight > (viewportHeight + 1)
    local scrollBar = scrollFrame.ScrollBar

    if scrollBar then
        if isUsingCustomSettingsWindowVisualStyle(scrollFrame) then
            scrollBar:Hide()
        else
            scrollBar:SetShown(needsScroll)
        end
    end

    settingsScrollRuntime.sync(scrollFrame, scrollChild, viewportHeight, contentHeight)

    scrollFrame:EnableMouseWheel(needsScroll)
    if needsScroll then
        scrollFrame:SetScript("OnMouseWheel", function(frame, delta)
            if settingsScrollRuntime.closeFloatingControlsForParentScroll(frame) then
                return
            end
            settingsScrollRuntime.noteFrameInteraction(frame)
            local minScroll, maxScroll = 0, math.max(0, (scrollChild:GetHeight() or 0) - (frame:GetHeight() or 0))
            local current = frame:GetVerticalScroll() or 0
            local nextScroll = current - (delta * 40)
            if nextScroll < minScroll then nextScroll = minScroll end
            if nextScroll > maxScroll then nextScroll = maxScroll end
            frame._paSkipNextCustomScrollBarHookSync = true
            frame:SetVerticalScroll(nextScroll)
            settingsScrollRuntime.sync(frame, scrollChild, frame:GetHeight() or 0, scrollChild:GetHeight() or 0)
        end)
    else
        scrollFrame:SetVerticalScroll(0)
        scrollFrame:SetScript("OnMouseWheel", nil)
    end
end
local function isSettingsControlParkedOffscreen(frame)
    if not frame or not frame.GetNumPoints or not frame.GetPoint then
        return false
    end

    local pointCount = tonumber(frame:GetNumPoints()) or 0
    for i = 1, pointCount do
        local _, _, _, x, y = frame:GetPoint(i)
        if type(x) == "number" and type(y) == "number" then
            if x <= -1000 or y <= -1000 then
                return true
            end
        end
    end

    return false
end

local function measureSettingsVisibleContentExtent(scrollChild, objects)
    if not scrollChild or type(objects) ~= "table" then
        return nil
    end

    local function safeFrameMetric(frame, methodName)
        if not frame then
            return nil
        end
        local method = frame[methodName]
        if type(method) ~= "function" then
            return nil
        end
        local ok, value = pcall(method, frame)
        if not ok then
            return nil
        end
        return tonumber(value) or nil
    end

    local rootTop = safeFrameMetric(scrollChild, "GetTop")
    if not rootTop or rootTop <= 0 then
        return nil
    end

    local maxExtent = nil
    for i = 1, #objects do
        local obj = objects[i]
        if obj and obj.GetBottom and (not obj.IsShown or obj:IsShown()) and not isSettingsControlParkedOffscreen(obj) then
            local objBottom = safeFrameMetric(obj, "GetBottom")
            if objBottom and objBottom > 0 then
                local extent = rootTop - objBottom
                if extent >= 0 and extent < 5000 then
                    if not maxExtent or extent > maxExtent then
                        maxExtent = extent
                    end
                end
            end
        end
    end

    return maxExtent
end

local function PA_PerfBegin(scopeName, explicitState)
    if PortalAuthority and PortalAuthority.PerfBegin then
        return PortalAuthority:PerfBegin(scopeName, explicitState)
    end
    return nil, nil
end

local function PA_PerfEnd(scopeName, startedAt, stateLabel)
    if startedAt ~= nil and PortalAuthority and PortalAuthority.PerfEnd then
        PortalAuthority:PerfEnd(scopeName, startedAt, stateLabel)
    end
end

local MAX_LAYOUT_RETRIES = 10
local function refreshWhenSized(frame, callback, attempt)
    if not frame or not callback then
        return
    end

    attempt = attempt or 0
    local width = frame:GetWidth() or 0
    local height = frame:GetHeight() or 0
    if width > 1 and height > 1 then
        callback()
        return
    end

    if attempt >= MAX_LAYOUT_RETRIES then
        callback()
        return
    end

    C_Timer.After(0, function()
        refreshWhenSized(frame, callback, attempt + 1)
    end)
end

local function withPanelHydration(panel, callback)
    if type(callback) ~= "function" then
        return
    end

    if not panel then
        callback()
        return
    end

    panel._paHydratingControls = (tonumber(panel._paHydratingControls) or 0) + 1
    local ok, err = pcall(callback)
    local remaining = (tonumber(panel._paHydratingControls) or 1) - 1
    if remaining > 0 then
        panel._paHydratingControls = remaining
    else
        panel._paHydratingControls = nil
    end
    if not ok then
        error(err, 0)
    end
end

local function isControlHydrating(frame)
    local current = frame
    while current do
        if (tonumber(current._paHydratingControls) or 0) > 0 then
            return true
        end
        current = current.GetParent and current:GetParent() or nil
    end
    return false
end

local function panelHasSizedViewport(frame)
    if not frame then
        return false
    end
    local width = tonumber(frame:GetWidth()) or 0
    local height = tonumber(frame:GetHeight()) or 0
    return width > 1 and height > 1
end

local function ensureSettingsCanvasPanelAnchored(panel)
    local parent = panel and panel.GetParent and panel:GetParent() or nil
    if not panel or not parent then
        return false
    end
    if panel._paAnchorParent ~= parent or not panel._paAnchored then
        panel:ClearAllPoints()
        panel:SetAllPoints(parent)
        panel._paAnchorParent = parent
    end
    panel._paAnchored = true
    return true
end

local SETTINGS_FIRST_OPEN_SHORT_SETTLE_DELAY = 0.05
local SETTINGS_FIRST_OPEN_VALID_KEYS = {
    root = true,
    announcements = true,
    dock = true,
    timers = true,
    interrupt = true,
    combat = true,
    keystone = true,
    profiles = true,
}
local settingsFirstOpenHost = {
    selectionToken = 0,
    selectedKey = nil,
    selectedPanel = nil,
    selectedGeneration = 0,
}
local function normalizeSettingsFirstOpenKey(key)
    if type(key) == "string" and SETTINGS_FIRST_OPEN_VALID_KEYS[key] then
        return key
    end
    return nil
end

local function setSettingsFirstOpenBootstrapActive(key)
    if PortalAuthority and PortalAuthority.SetSettingsBootstrapActive then
        PortalAuthority:SetSettingsBootstrapActive(normalizeSettingsFirstOpenKey(key) or "none")
    end
end

local function getSettingsFirstOpenGeneration(panel)
    return math.max(0, tonumber(panel and panel._paFirstOpenGeneration) or 0)
end

local function getSettingsFirstOpenState(panel)
    return tostring(panel and panel._paFirstOpenState or "idle")
end

local function isSettingsFirstOpenReady(panel)
    return getSettingsFirstOpenState(panel) == "ready"
end

local function isSettingsFirstOpenSettlePending(panel)
    return getSettingsFirstOpenState(panel) == "pending"
end

local function setSelectedSettingsFirstOpenPanel(panel)
    local key = normalizeSettingsFirstOpenKey(panel and panel._settingsFirstOpenKey)
    settingsFirstOpenHost.selectionToken = (tonumber(settingsFirstOpenHost.selectionToken) or 0) + 1
    settingsFirstOpenHost.selectedPanel = panel
    settingsFirstOpenHost.selectedKey = key
    settingsFirstOpenHost.selectedGeneration = getSettingsFirstOpenGeneration(panel)
    return settingsFirstOpenHost.selectionToken
end

local function clearSelectedSettingsFirstOpenPanel(panel)
    if panel and settingsFirstOpenHost.selectedPanel ~= panel then
        return
    end
    settingsFirstOpenHost.selectionToken = (tonumber(settingsFirstOpenHost.selectionToken) or 0) + 1
    settingsFirstOpenHost.selectedPanel = nil
    settingsFirstOpenHost.selectedKey = nil
    settingsFirstOpenHost.selectedGeneration = 0
    setSettingsFirstOpenBootstrapActive("none")
end

local function resetSettingsFirstOpenPanelState(panel)
    if not panel then
        return 0
    end
    local generation = getSettingsFirstOpenGeneration(panel) + 1
    panel._paFirstOpenGeneration = generation
    panel._paFirstOpenState = "idle"
    panel._paFirstOpenSelectionToken = nil
    panel._paFirstOpenSizeReadyConsumed = nil
    panel._paFirstOpenShortSettleQueued = nil
    panel._paFirstOpenImmediateSizeCheckQueued = nil
    if settingsFirstOpenHost.selectedPanel == panel then
        settingsFirstOpenHost.selectedGeneration = generation
        setSettingsFirstOpenBootstrapActive("none")
    end
    return generation
end

local function shouldBailSettingsFirstOpenSettle(panel, generation, selectionToken)
    if not panel then
        return true
    end
    if getSettingsFirstOpenState(panel) ~= "pending" then
        return true
    end
    if getSettingsFirstOpenGeneration(panel) ~= generation then
        return true
    end
    if panel._paSettingsClosing then
        return true
    end
    if not panel.IsShown or not panel:IsShown() then
        return true
    end
    if settingsFirstOpenHost.selectedPanel ~= panel then
        return true
    end
    if settingsFirstOpenHost.selectedKey ~= normalizeSettingsFirstOpenKey(panel._settingsFirstOpenKey) then
        return true
    end
    if (tonumber(settingsFirstOpenHost.selectionToken) or 0) ~= (tonumber(selectionToken) or 0) then
        return true
    end
    if (tonumber(settingsFirstOpenHost.selectedGeneration) or 0) ~= generation then
        return true
    end
    return false
end

local function evaluateSettingsFirstOpenReady(panel)
    if not panel then
        return false
    end
    local predicate = panel._settingsFirstOpenIsReady
    if type(predicate) == "function" then
        local ok, ready = pcall(predicate, panel)
        return ok and ready == true
    end
    return panelHasSizedViewport(panel._settingsFirstOpenSizeTarget or panel._sizingFrame or panel)
end

local function runSettingsFirstOpenRender(panel, reason)
    if not panel then
        return false
    end
    local render = panel._settingsFirstOpenRender
    if type(render) ~= "function" then
        return false
    end
    withPanelHydration(panel, function()
        render(panel, reason or "on_show")
    end)
    return true
end

local function completeSettingsFirstOpenSettle(panel, generation, ready)
    if not panel then
        return false
    end
    if getSettingsFirstOpenState(panel) ~= "pending" then
        return false
    end
    if getSettingsFirstOpenGeneration(panel) ~= generation then
        return false
    end

    panel._paFirstOpenState = ready and "ready" or "idle"
    panel._paFirstOpenGeneration = generation + 1
    panel._paFirstOpenSelectionToken = nil
    panel._paFirstOpenSizeReadyConsumed = nil
    panel._paFirstOpenShortSettleQueued = nil
    panel._paFirstOpenImmediateSizeCheckQueued = nil
    if settingsFirstOpenHost.selectedPanel == panel then
        settingsFirstOpenHost.selectedGeneration = panel._paFirstOpenGeneration
    end
    setSettingsFirstOpenBootstrapActive("none")
    return true
end

local function queueSettingsFirstOpenShortSettle(panel, generation, selectionToken)
    if not panel or panel._paFirstOpenShortSettleQueued then
        return
    end
    panel._paFirstOpenShortSettleQueued = true

    local function run()
        if panel then
            panel._paFirstOpenShortSettleQueued = nil
        end
        if shouldBailSettingsFirstOpenSettle(panel, generation, selectionToken) then
            return
        end

        if not panel._paFirstOpenSizeReadyConsumed then
            local sizeTarget = panel._settingsFirstOpenSizeTarget or panel._sizingFrame or panel
            if panelHasSizedViewport(sizeTarget) then
                panel._paFirstOpenSizeReadyConsumed = true
                runSettingsFirstOpenRender(panel, "size_changed")
                if evaluateSettingsFirstOpenReady(panel) then
                    completeSettingsFirstOpenSettle(panel, generation, true)
                    return
                end
            else
                completeSettingsFirstOpenSettle(panel, generation, false)
                return
            end
        end

        runSettingsFirstOpenRender(panel, "size_changed")
        if evaluateSettingsFirstOpenReady(panel) then
            completeSettingsFirstOpenSettle(panel, generation, true)
            return
        end
        completeSettingsFirstOpenSettle(panel, generation, false)
    end

    if C_Timer and C_Timer.After then
        C_Timer.After(SETTINGS_FIRST_OPEN_SHORT_SETTLE_DELAY, run)
    else
        run()
    end
end

local function handleSettingsFirstOpenSizeReady(panel, reason)
    if not isSettingsFirstOpenSettlePending(panel) then
        return false
    end

    local generation = getSettingsFirstOpenGeneration(panel)
    local selectionToken = tonumber(panel and panel._paFirstOpenSelectionToken) or 0
    if shouldBailSettingsFirstOpenSettle(panel, generation, selectionToken) then
        return false
    end

    local sizeTarget = panel and (panel._settingsFirstOpenSizeTarget or panel._sizingFrame or panel) or nil
    if not panelHasSizedViewport(sizeTarget) then
        return false
    end
    if panel._paFirstOpenSizeReadyConsumed then
        return true
    end

    panel._paFirstOpenSizeReadyConsumed = true
    runSettingsFirstOpenRender(panel, reason or "size_changed")
    if evaluateSettingsFirstOpenReady(panel) then
        completeSettingsFirstOpenSettle(panel, generation, true)
        return true
    end

    queueSettingsFirstOpenShortSettle(panel, generation, selectionToken)
    return true
end

local function queueSettingsFirstOpenImmediateSizeCheck(panel, generation, selectionToken)
    if not panel or panel._paFirstOpenImmediateSizeCheckQueued then
        return
    end
    panel._paFirstOpenImmediateSizeCheckQueued = true

    local function run()
        if panel then
            panel._paFirstOpenImmediateSizeCheckQueued = nil
        end
        if shouldBailSettingsFirstOpenSettle(panel, generation, selectionToken) then
            return
        end

        local sizeTarget = panel and (panel._settingsFirstOpenSizeTarget or panel._sizingFrame or panel) or nil
        if not panelHasSizedViewport(sizeTarget) then
            return
        end

        if panel._paFirstOpenSizeReadyConsumed then
            runSettingsFirstOpenRender(panel, "size_changed")
            if evaluateSettingsFirstOpenReady(panel) then
                completeSettingsFirstOpenSettle(panel, generation, true)
                return
            end
            queueSettingsFirstOpenShortSettle(panel, generation, selectionToken)
            return
        end

        handleSettingsFirstOpenSizeReady(panel, "size_changed")
    end

    if C_Timer and C_Timer.After then
        C_Timer.After(0, run)
    else
        run()
    end
end

local function startSettingsFirstOpenSettle(panel)
    if not panel or type(panel._settingsFirstOpenRender) ~= "function" then
        return false
    end
    if isSettingsFirstOpenReady(panel) then
        return false
    end

    local generation = getSettingsFirstOpenGeneration(panel) + 1
    panel._paFirstOpenGeneration = generation
    panel._paFirstOpenState = "pending"
    panel._paFirstOpenSelectionToken = nil
    panel._paFirstOpenSizeReadyConsumed = nil
    panel._paFirstOpenShortSettleQueued = nil
    panel._paFirstOpenImmediateSizeCheckQueued = nil
    panel._paSettingsClosing = false

    local selectionToken = setSelectedSettingsFirstOpenPanel(panel)
    panel._paFirstOpenSelectionToken = selectionToken
    setSettingsFirstOpenBootstrapActive(panel._settingsFirstOpenKey)

    runSettingsFirstOpenRender(panel, "on_show")
    if evaluateSettingsFirstOpenReady(panel) then
        completeSettingsFirstOpenSettle(panel, generation, true)
        return true
    end

    queueSettingsFirstOpenImmediateSizeCheck(panel, generation, selectionToken)
    queueSettingsFirstOpenShortSettle(panel, generation, selectionToken)
    return true
end

local function shouldSuppressSettingsFirstOpenExternalRefresh(panel)
    return isSettingsFirstOpenSettlePending(panel)
end

local SETTINGS_WINDOW_CHROME_PARK_X = -5000
local SETTINGS_WINDOW_CHROME_PARK_Y = -5000

local function isPanelUsingCustomSettingsWindowHost(panel)
    if not panel or not PortalAuthority or not PortalAuthority.IsSettingsWindowHostEnabled then
        return false
    end
    if not PortalAuthority:IsSettingsWindowHostEnabled() then
        return false
    end
    if panel._paHostedBySettingsWindow == true then
        return true
    end

    local runtime = PortalAuthority._settingsWindowRuntime
    local body = runtime and runtime.body or nil
    if body and panel.GetParent and panel:GetParent() == body then
        return true
    end

    return false
end

local function setSettingsWindowChromeVisible(widget, visible, anchorParent)
    if not widget then
        return
    end

    if visible then
        if widget.Show then
            widget:Show()
        end
        return
    end

    if widget.Hide then
        widget:Hide()
    end
    if widget.ClearAllPoints then
        widget:ClearAllPoints()
    end
    if anchorParent and widget.SetPoint then
        widget:SetPoint("TOPLEFT", anchorParent, "TOPLEFT", SETTINGS_WINDOW_CHROME_PARK_X, SETTINGS_WINDOW_CHROME_PARK_Y)
    end
end

local function refreshSettingsWindowHeaderAccessoryIfHosted(panel)
    if not panel or not isPanelUsingCustomSettingsWindowHost(panel) then
        return
    end
    if PortalAuthority and PortalAuthority.RefreshSettingsWindowHeaderAccessory then
        PortalAuthority:RefreshSettingsWindowHeaderAccessory()
    end
end

local function handleSettingsFirstOpenPanelShow(panel)
    if not panel then
        return false
    end
    panel._paSettingsClosing = false
    if panel._paSearchSectionKey then
        local hadPendingRealActivation = panel._paSearchRealActivationPending == true
        panel._paSearchRealActivationPending = false
        panel._paSearchRealActivationComplete = true
        if hadPendingRealActivation and (panel._paSearchHiddenIndexBuilt == true or panel._paSearchHasProvisionalControlTargets == true) then
            panel._paSearchRegistryDirty = true
        end
    end
    if isSettingsFirstOpenReady(panel) then
        setSelectedSettingsFirstOpenPanel(panel)
        setSettingsFirstOpenBootstrapActive("none")
        return false
    end
    return startSettingsFirstOpenSettle(panel)
end

local function handleSettingsFirstOpenPanelHide(panel)
    if not panel then
        return
    end
    panel._paSettingsClosing = true
    clearSelectedSettingsFirstOpenPanel(panel)
    if isSettingsFirstOpenSettlePending(panel) then
        resetSettingsFirstOpenPanelState(panel)
    end
end

local function refreshPanel(panel)
    if panel and panel._refresh then
        refreshWhenSized(panel._sizingFrame or panel, function()
            if not panel or not panel:IsShown() then
                return
            end
            withPanelHydration(panel, panel._refresh)
        end)
    end
end

local function createScrollableTextArea(parent)
    local container = CreateFrame("Frame", nil, parent)
    container:SetClipsChildren(true)

    local scrollFrame = CreateFrame("ScrollFrame", nil, container, "UIPanelScrollFrameTemplate")
    scrollFrame:SetPoint("TOPLEFT", container, "TOPLEFT", 6, -6)
    scrollFrame:SetPoint("BOTTOMRIGHT", container, "BOTTOMRIGHT", -26, 6)

    local editBox = CreateFrame("EditBox", nil, scrollFrame)
    editBox:SetAutoFocus(false)
    editBox:SetMultiLine(true)
    editBox:SetWordWrap(true)
    if editBox.SetNonSpaceWrap then
        editBox:SetNonSpaceWrap(true)
    end
    editBox:SetTextInsets(8, 8, 8, 8)
    editBox:SetJustifyH("LEFT")
    editBox:SetJustifyV("TOP")
    editBox:SetWidth(1)
    editBox:SetHeight(1)
    applyGlobalFontToObject(editBox, 12)

    scrollFrame:SetScrollChild(editBox)
    markSettingsWindowVisualOptIn(container)
    markSettingsWindowVisualOptIn(scrollFrame)
    markSettingsWindowVisualOptIn(editBox)
    registerSettingsWindowVisualRefresh(scrollFrame, function(selfScroll)
        local child = selfScroll.GetScrollChild and selfScroll:GetScrollChild() or editBox
        local viewportHeight = tonumber(selfScroll:GetHeight()) or 0
        local contentHeight = child and (tonumber(child:GetHeight()) or 0) or 0
        settingsScrollRuntime.sync(selfScroll, child, viewportHeight, contentHeight)
    end)

    local function updateTextBounds()
        local width = math.max(1, scrollFrame:GetWidth() - 4)
        editBox:SetWidth(width)
        local contentHeight = math.max(scrollFrame:GetHeight(), editBox:GetStringHeight() + 20)
        editBox:SetHeight(contentHeight)
        settingsScrollRuntime.sync(scrollFrame, editBox, scrollFrame:GetHeight() or 0, contentHeight)
    end

    scrollFrame:SetScript("OnSizeChanged", function()
        updateTextBounds()
    end)

    scrollFrame:EnableMouseWheel(true)
    scrollFrame:SetScript("OnMouseWheel", function(frame, delta)
        settingsScrollRuntime.noteFrameInteraction(frame)
        local minScroll, maxScroll = 0, math.max(0, (editBox:GetHeight() or 0) - (frame:GetHeight() or 0))
        local nextScroll = (frame:GetVerticalScroll() or 0) - (delta * 24)
        if nextScroll < minScroll then nextScroll = minScroll end
        if nextScroll > maxScroll then nextScroll = maxScroll end
        frame._paSkipNextCustomScrollBarHookSync = true
        frame:SetVerticalScroll(nextScroll)
        settingsScrollRuntime.sync(frame, editBox, frame:GetHeight() or 0, editBox:GetHeight() or 0)
    end)

    editBox:SetScript("OnTextChanged", function()
        updateTextBounds()
    end)

    container.ScrollFrame = scrollFrame
    container.EditBox = editBox
    container.UpdateLayout = updateTextBounds

    return container
end
local SETTINGS_SECTION_DESCRIPTOR_FLOOR = 11
local SETTINGS_SECTION_DESCRIPTOR_BOTTOM_GAP = 10

local function createSettingsSectionDescriptor(parent, text)
    local fs = createStyledFontString(parent, "ARTWORK", "GameFontHighlightSmall", 11)
    fs:SetText(tostring(text or ""))
    fs:SetTextColor(0.62, 0.64, 0.68, 0.92)
    fs:SetJustifyH("LEFT")
    fs:SetJustifyV("TOP")
    fs:SetWordWrap(true)
    return fs
end

local function layoutSettingsSectionDescriptor(descriptor, parent, left, topY, width)
    if not descriptor then
        return topY
    end
    local measuredWidth = math.max(1, math.floor(tonumber(width) or 0))
    descriptor:ClearAllPoints()
    descriptor:SetPoint("TOPLEFT", parent, "TOPLEFT", left, topY)
    descriptor:SetWidth(measuredWidth)
    local descriptorH = math.max(
        SETTINGS_SECTION_DESCRIPTOR_FLOOR,
        math.ceil(tonumber((descriptor.GetStringHeight and descriptor:GetStringHeight()) or 0) or 0)
    )
    return topY - descriptorH - SETTINGS_SECTION_DESCRIPTOR_BOTTOM_GAP
end
OptionsInternal.createScrollableChild = createScrollableChild
OptionsInternal.updateScrollBehavior = updateScrollBehavior
OptionsInternal.withPanelHydration = withPanelHydration
OptionsInternal.resetSettingsFirstOpenPanelState = resetSettingsFirstOpenPanelState
OptionsInternal.panelHasSizedViewport = panelHasSizedViewport
OptionsInternal.ensureSettingsCanvasPanelAnchored = ensureSettingsCanvasPanelAnchored
OptionsInternal.measureSettingsVisibleContentExtent = measureSettingsVisibleContentExtent
OptionsInternal.isControlHydrating = isControlHydrating
OptionsInternal.handleSettingsFirstOpenSizeReady = handleSettingsFirstOpenSizeReady
OptionsInternal.handleSettingsFirstOpenPanelShow = handleSettingsFirstOpenPanelShow
OptionsInternal.handleSettingsFirstOpenPanelHide = handleSettingsFirstOpenPanelHide
OptionsInternal.shouldSuppressSettingsFirstOpenExternalRefresh = shouldSuppressSettingsFirstOpenExternalRefresh
OptionsInternal.isPanelUsingCustomSettingsWindowHost = isPanelUsingCustomSettingsWindowHost
OptionsInternal.setSettingsWindowChromeVisible = setSettingsWindowChromeVisible
OptionsInternal.refreshSettingsWindowHeaderAccessoryIfHosted = refreshSettingsWindowHeaderAccessoryIfHosted
OptionsInternal.refreshPanel = refreshPanel
OptionsInternal.createScrollableTextArea = createScrollableTextArea
OptionsInternal.createSettingsSectionDescriptor = createSettingsSectionDescriptor
OptionsInternal.layoutSettingsSectionDescriptor = layoutSettingsSectionDescriptor
OptionsInternal.settingsFirstOpenHost = settingsFirstOpenHost
