local addonName, MSUF = ...
MSUF = MSUF or {}
local M = MSUF.MSUF2 or {}
MSUF.MSUF2 = M

-- Menu2 Group Auras page.
-- Builds party/raid aura lane controls. Auras3 refreshes native 12.1 container layout
-- after these settings change; Blizzard owns live filtering, assignment, and icon updates.
local W = M.Widgets
local T = M.Theme or {}
local GP = M.GroupPage or {}
local floor, ceil, abs = math.floor, math.ceil, math.abs
local max = math.max
local min = math.min
local C_Timer = _G.C_Timer
local VT = M.ValueTextList
local AURA_ANCHORS, STATUS_ICON_ANCHORS, SPELL_GROWTH_VALUES, ScopeSection, CurrentScope, AuraGroup, AurasRoot, QueueGF, RefreshContext, BindNestedSlider, BindNestedStrataSlider, BindNestedDropdown, SetOptionEnabled, SetOptionsEnabled, FinalizeScopePage, SetSectionBadgesAndStatus, OnOffBadge, BadgeNumber, OptionText, FrameStrataCount = M.Pick(GP, [[AURA_ANCHORS STATUS_ICON_ANCHORS SPELL_GROWTH_VALUES ScopeSection CurrentScope AuraGroup AurasRoot QueueGF RefreshContext BindNestedSlider BindNestedStrataSlider BindNestedDropdown SetOptionEnabled SetOptionsEnabled FinalizeScopePage SetSectionBadgesAndStatus OnOffBadge BadgeNumber OptionText FrameStrataCount]])
AURA_ANCHORS = AURA_ANCHORS or {}
STATUS_ICON_ANCHORS = STATUS_ICON_ANCHORS or {}
SPELL_GROWTH_VALUES = SPELL_GROWTH_VALUES or {}
SetSectionBadgesAndStatus = SetSectionBadgesAndStatus or M.Noop
OnOffBadge = OnOffBadge or M.OnOffBadge
BadgeNumber = BadgeNumber or M.BadgeNumber
OptionText = OptionText or function(values, value, fallback)
    values = type(values) == "function" and values() or values
    if type(values) == "table" then
        for i = 1, #values do
            local row = values[i]
            if row and row.value == value then return row.text or row.label or tostring(value) end
        end
    end
    return fallback or tostring(value or "")
end
local function ThemeColor(key, fallback)
    local colors = T and T.colors
    return colors and colors[key] or fallback
end
local function AuraCatalogToken(value, fallback)
    local token = tostring(value or ""):lower():gsub("[^%w]+", "-"):gsub("^%-+", ""):gsub("%-+$", "")
    return token ~= "" and token or (fallback or "control")
end
local function AuraCatalogPageKey(value, fallback)
    local token = tostring(value or ""):lower():gsub("[^%w_%-]+", "-"):gsub("^%-+", ""):gsub("%-+$", "")
    return token ~= "" and token or (fallback or "gf_auras")
end
local function AuraControlMeta(ctx, path, classification)
    path = tostring(path or "control"):lower():gsub("[^%w%._/-]+", "-")
    path = path:gsub("/", "."):gsub("^%.+", ""):gsub("%.+$", "")
    local pageKey = AuraCatalogPageKey(ctx and ctx.key or M.activeKey, "gf_auras")
    local identity = "auras." .. path
    local meta = {
        controlId = "menu2." .. pageKey .. "." .. identity,
        pageKey = pageKey,
        identityKey = identity,
        controlPath = "auras/" .. path:gsub("%.", "/"),
        classification = classification or "setting",
        ephemeral = classification == "ephemeral" or nil,
    }
    return meta
end
local function RegisterAuraControl(ctx, widget, label, kind, path, classification, navigationKey)
    if not widget or type(M.RegisterSearchWidget) ~= "function" then return widget end
    local meta = AuraControlMeta(ctx, path, classification)
    meta.label = label
    meta.kind = kind
    if classification == "navigation" then meta.navigationKey = navigationKey end
    M.RegisterSearchWidget(widget, meta)
    return widget
end
local MUTED = ThemeColor("muted", { 0.55, 0.66, 0.82, 0.92 })
local GF_AURA_WORKSPACE_TOOLS = {
    { value = "layout", text = "Layout" },
    { value = "filters", text = "Filters" },
    { value = "blacklist", text = "Blacklist" },
}
local GF_AURA_WORKSPACE_LANES = {
    { value = "buff", text = "Buffs" },
    { value = "debuff", text = "Debuffs" },
}
local GF_AURA_WORKSPACE_TOOL_OK = { layout = true, filters = true, blacklist = true }
local function CurrentAuraWorkspaceTool(scope, lane)
    M.gfAuraToolSelection = M.gfAuraToolSelection or {}
    local scopeState = M.gfAuraToolSelection[scope]
    if type(scopeState) ~= "table" then scopeState = {}; M.gfAuraToolSelection[scope] = scopeState end
    local tool = scopeState[lane]
    if not GF_AURA_WORKSPACE_TOOL_OK[tool] then tool = "layout"; scopeState[lane] = tool end
    return tool
end
local function SetAuraWorkspaceTool(scope, lane, tool)
    M.gfAuraToolSelection = M.gfAuraToolSelection or {}
    local scopeState = M.gfAuraToolSelection[scope]
    if type(scopeState) ~= "table" then scopeState = {}; M.gfAuraToolSelection[scope] = scopeState end
    scopeState[lane] = GF_AURA_WORKSPACE_TOOL_OK[tool] and tool or "layout"
end
local function CurrentAuraWorkspaceLane(scope)
    M.gfAuraLaneSelection = M.gfAuraLaneSelection or {}
    local lane = M.gfAuraLaneSelection[scope]
    if lane ~= "buff" and lane ~= "debuff" then lane = "buff"; M.gfAuraLaneSelection[scope] = lane end
    return lane
end
local function SetAuraWorkspaceLane(scope, lane)
    M.gfAuraLaneSelection = M.gfAuraLaneSelection or {}
    M.gfAuraLaneSelection[scope] = lane == "debuff" and "debuff" or "buff"
end
local groupAuraRebuildSerial = 0
local function RestoreGroupAuraScroll(offset, key, serial)
    if M.activeKey ~= key or serial ~= groupAuraRebuildSerial then return end
    local scroll = M.scrollFrame
    if not (scroll and scroll.SetVerticalScroll) then return end
    local range = scroll.GetVerticalScrollRange and scroll:GetVerticalScrollRange() or offset
    scroll:SetVerticalScroll(min(max(tonumber(offset) or 0, 0), max(tonumber(range) or 0, 0)))
    if M.RefreshPinnedPreviews then M.RefreshPinnedPreviews(scroll) end
end
local function RebuildGroupAuraPage(ctx)
    local key = (ctx and ctx.key) or M.activeKey or "gf_auras"
    if not (M.InvalidatePage and M.SelectPage and M.frame and M.frame.IsShown and M.frame:IsShown()) then return end
    local offset = M.scrollFrame and M.scrollFrame.GetVerticalScroll and M.scrollFrame:GetVerticalScroll() or 0
    groupAuraRebuildSerial = groupAuraRebuildSerial + 1
    local serial = groupAuraRebuildSerial
    M.InvalidatePage(key)
    M.activeKey = nil
    M.SelectPage(key)
    RestoreGroupAuraScroll(offset, key, serial)
    if C_Timer and C_Timer.After then
        C_Timer.After(0, function() RestoreGroupAuraScroll(offset, key, serial) end)
        C_Timer.After(0.05, function() RestoreGroupAuraScroll(offset, key, serial) end)
    end
end
local function BuildAuraWorkspaceTabs(ctx, section, scope, lane, width)
    local sectionW = tonumber(width) or 720
    local laneBar = W.ScopeOverrideBar(ctx, section, {
        values = GF_AURA_WORKSPACE_LANES,
        width = sectionW,
        label = "Container:",
        labelWidth = 72,
        centerY = -28,
        getValue = function() return CurrentAuraWorkspaceLane(scope) end,
        setValue = function(value)
            if CurrentAuraWorkspaceLane(scope) == value then return end
            SetAuraWorkspaceLane(scope, value)
            RebuildGroupAuraPage(ctx)
        end,
    })
    RegisterAuraControl(ctx, laneBar, "Container", "segment", "group-workspace.container-selector", "ephemeral")
    local toolBar = W.ScopeOverrideBar(ctx, section, {
        values = GF_AURA_WORKSPACE_TOOLS,
        width = sectionW,
        label = "Edit:",
        labelWidth = 72,
        centerY = -62,
        getValue = function() return CurrentAuraWorkspaceTool(scope, lane) end,
        setValue = function(value)
            if CurrentAuraWorkspaceTool(scope, lane) == value then return end
            SetAuraWorkspaceTool(scope, lane, value)
            RebuildGroupAuraPage(ctx)
        end,
    })
    RegisterAuraControl(ctx, toolBar, "Edit", "segment",
        "group-workspace.lane." .. AuraCatalogToken(lane, "lane") .. ".tool-selector", "ephemeral")
    local openStyle = T.Button(section, "Open Aura Style", 126, 22)
    openStyle:SetPoint("TOPRIGHT", section, "TOPRIGHT", -16, -74)
    if T.CenterButtonLabel then T.CenterButtonLabel(openStyle) end
    openStyle:SetScript("OnClick", function()
        local styleScope = scope == "mythicraid" and "raid" or scope
        M.SetMenuStateValue("auraScope", styleScope)
        M.SetMenuStateValue("auraStyleGFScope", styleScope)
        M.SetMenuStateValue("auraStyleContainer", lane)
        M.SetMenuStateValue("auraStyleGFLane", lane)
        if M.SelectPage then M.SelectPage("auras3_styling") end
    end)
    RegisterAuraControl(ctx, openStyle, "Open Aura Style", "button", "group-workspace.open-aura-style", "navigation", "auras3_styling")
    W.Text(section, "All icon styling: Appearance > Auras.", 16, -84, sectionW - 174, MUTED)
end
local function NativeAuraKey(groupKey)
    return groupKey == "buff" and "buffs" or "debuffs"
end
local function LaneBackendEnabled(scope, groupKey)
    local root = AurasRoot and AurasRoot(scope)
    local group = AuraGroup(scope, groupKey)
    if not root then return group.enabled ~= false end
    return root.enabled ~= false and group.enabled ~= false
end
local function BindAuraLaneEnabled(ctx, widget, groupKey)
    M.BindBoolWidget(ctx, widget,
        function()
            return LaneBackendEnabled(CurrentScope(), groupKey)
        end,
        function(v)
            local scope = CurrentScope()
            local root = AurasRoot and AurasRoot(scope)
            local group = AuraGroup(scope, groupKey)
            local enabled = v and true or false
            if root then
                root.enabled = true
                root.blizzardTypes = root.blizzardTypes or {}
                root.blizzardTypes[NativeAuraKey(groupKey)] = false
            end
            group.enabled = enabled
            if QueueGF then QueueGF(scope, "auras") end
            M.CallIf(RefreshContext, ctx)
        end,
        AuraControlMeta(ctx, "group-workspace.lane." .. AuraCatalogToken(groupKey, "lane") .. ".enabled"))
    return widget
end
local function CreateNestedGroupAuraBuilder(ctx, parentBuilder, body)
    local entry = body and body._msuf2CollapsibleEntry
    if not (entry and W.PageBuilder) then return parentBuilder end
    local bodyWidth = body._msuf2Width or parentBuilder.width or 720
    local nestedCtx = setmetatable({
        wrapper = body,
        width = max(320, bodyWidth - 24),
        key = ctx and ctx.key,
        entry = ctx and ctx.entry,
        _msuf2ContentX = 12,
        _msuf2TopInset = 0,
    }, { __index = ctx })
    function nestedCtx:SetContentHeight(height)
        height = max(80, ceil(tonumber(height) or 80))
        if entry.contentHeight == height then return end
        entry.contentHeight = height
        body:SetHeight(height)
        if parentBuilder.RequestRelayoutCollapsibles then parentBuilder:RequestRelayoutCollapsibles() end
    end
    local nestedBuilder = W.PageBuilder(nestedCtx)
    entry._msuf2SettleContentLayout = function()
        if nestedBuilder.RelayoutCollapsibles then nestedBuilder:RelayoutCollapsibles() end
        nestedCtx:SetContentHeight(abs(nestedBuilder.y) + 42)
    end
    return nestedBuilder
end

local function BuildGFAuras(ctx)
    local b = W.PageBuilder(ctx)
    ScopeSection(ctx, b)
    M.GroupPreview.Add(ctx, b)
    local function RefreshPage() M.CallIf(M.SelectPage, ctx.key) end
    local scope = CurrentScope()
    local lane = CurrentAuraWorkspaceLane(scope)
    local tool = CurrentAuraWorkspaceTool(scope, lane)
    local anchors = (#STATUS_ICON_ANCHORS > 0 and STATUS_ICON_ANCHORS) or AURA_ANCHORS
    local growthValues = (#SPELL_GROWTH_VALUES > 0 and SPELL_GROWTH_VALUES)
        or VT("RIGHTDOWN", "Right then Down", "LEFTDOWN", "Left then Down", "RIGHTUP", "Right then Up", "LEFTUP", "Left then Up")
    local defaults = lane == "buff"
        and { anchor = "BOTTOMRIGHT", growth = "LEFTUP", size = 22, perRow = 4, max = 6, spacing = 1, layer = 5 }
        or { anchor = "TOPLEFT", growth = "RIGHTDOWN", size = 20, perRow = 3, max = 6, spacing = 1, layer = 6 }

    local outer = b:CollapsibleSection("auras", "Auras", 120, false)
    local auraBuilder = CreateNestedGroupAuraBuilder(ctx, b, outer)
    local top = auraBuilder:Section("", 104)
    if top.title then top.title:Hide() end
    BuildAuraWorkspaceTabs(ctx, top, scope, lane, top._msuf2Width or auraBuilder.width or 720)

    if tool == "layout" then
        local title = lane == "debuff" and "Debuff Layout" or "Buff Layout"
        local section = auraBuilder:Section(title, 190)
        local w = section._msuf2Width or auraBuilder.width or 720
        local inner, gap = w - 48, 10
        local controls = {}
        local enable = BindAuraLaneEnabled(ctx, W.SwitchAt(section, "Visible", 24, -62, 104), lane)
        enable._msuf2GroupFrameGateAlwaysEnabled = true
        local dropdownW = max(180, floor((inner - 126 - gap * 2) / 2))
        local anchorX = 24 + 126 + gap
        local growthX = anchorX + dropdownW + gap
        local function Dropdown(label, x, values, key, fallback)
            local widget = BindNestedDropdown(ctx, W.Dropdown(section, label, values, dropdownW),
                function() return AuraGroup(CurrentScope(), lane) end, key, fallback, "auras",
                AuraControlMeta(ctx, "group-workspace.lane." .. AuraCatalogToken(lane) .. ".layout." .. AuraCatalogToken(key)))
            W.MoveWidget(widget, section, x, -34, dropdownW, "LEFT")
            controls[#controls + 1] = widget
        end
        Dropdown("Anchor", anchorX, anchors, "anchor", defaults.anchor)
        Dropdown("Growth", growthX, growthValues, "growth", defaults.growth)
        local col4 = floor((inner - gap * 3) / 4)
        local function Slider(label, col, y, minValue, maxValue, key, fallback)
            local widget = BindNestedSlider(ctx, W.Slider(section, label, minValue, maxValue, 1, col4),
                function() return AuraGroup(CurrentScope(), lane) end, key, fallback, "auras",
                AuraControlMeta(ctx, "group-workspace.lane." .. AuraCatalogToken(lane) .. ".layout." .. AuraCatalogToken(key)))
            W.MoveWidget(widget, section, 24 + (col - 1) * (col4 + gap), y, col4)
            controls[#controls + 1] = widget
        end
        Slider("X", 1, -92, -300, 300, "x", 0)
        Slider("Y", 2, -92, -300, 300, "y", 0)
        Slider("Max", 3, -92, 0, 20, "max", defaults.max)
        Slider("Size", 4, -92, 8, 80, "size", defaults.size)
        Slider("Per row", 1, -146, 1, 20, "perRow", defaults.perRow)
        Slider("Gap", 2, -146, 0, 12, "spacing", defaults.spacing)
        Slider("Layer", 3, -146, 0, 30, "layer", defaults.layer)
        local strata = BindNestedStrataSlider(ctx, W.Slider(section, "Strata", 0, (FrameStrataCount or 9) - 1, 1, col4),
            function() return AuraGroup(CurrentScope(), lane) end, "strata", "AUTO", "auras",
            AuraControlMeta(ctx, "group-workspace.lane." .. AuraCatalogToken(lane) .. ".layout.strata"))
        W.MoveWidget(strata, section, 24 + 3 * (col4 + gap), -146, col4)
        controls[#controls + 1] = strata
        M.TrackRefresh(ctx, function()
            local shown = LaneBackendEnabled(CurrentScope(), lane)
            SetOptionEnabled(enable, true)
            SetOptionsEnabled(controls, shown)
        end)
    elseif type(M.BuildAuras3GroupLaneWorkspace) == "function" then
        M.BuildAuras3GroupLaneWorkspace(ctx, auraBuilder, scope, lane, { tool = tool, compact = true })
    end

    if GP.BuildSpellIndicatorsSection then GP.BuildSpellIndicatorsSection(ctx, b, RefreshPage) end
    FinalizeScopePage(ctx, b)
end
M.RegisterPage("gf_auras", { title = "MSUF Group Auras", build = BuildGFAuras, version = 27 })
