local ADDON_NAME, ns = ...
ns = ns or {}
local Const = ns.Const
local Util = ns.Util
local Remix = ns.Remix
local Controls = ns.Controls

local Options = {}
ns.Options = Options

local function initLegacyScrollFrame(scrollFrame, panExtent)
    if Controls and Controls.initLegacyScrollFrame then
        Controls.initLegacyScrollFrame(scrollFrame, panExtent)
    elseif scrollFrame then
        scrollFrame:EnableMouseWheel(true)
    end
end

local function createCheck(parent, label, initial, onClick)
    local cb = CreateFrame("CheckButton", nil, parent, "InterfaceOptionsCheckButtonTemplate")
    cb.Text:SetText(label)
    cb:SetChecked(initial)
    cb:SetScript("OnClick", function(self) onClick(not not self:GetChecked()) end)
    return cb
end

local function createSlider(parent, label, min, max, step, value, onChanged)
    local holder = CreateFrame("Frame", nil, parent, "MinimalSliderWithSteppersTemplate")
    local slider = holder and holder.Slider
    if holder and slider then
        holder:SetFrameLevel((parent:GetFrameLevel() or 1) + 2)
        slider:EnableMouse(true)
        slider:SetMinMaxValues(min, max)
        slider:SetValueStep(step)
        slider:SetObeyStepOnDrag(true)
        slider:SetValue(value)
        holder._min, holder._max, holder._step = min, max, step
        holder.Text = holder.Text or holder:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
        holder.Text:ClearAllPoints()
        holder.Text:SetPoint("BOTTOM", holder, "TOP", 0, 2)
        holder.Text:SetText(label .. ": " .. value)
        if holder.Low and holder.High then
            holder.Low:ClearAllPoints()
            holder.Low:SetPoint("TOPLEFT", holder, "BOTTOMLEFT", 0, -8)
            holder.Low:SetText(min)
            holder.High:ClearAllPoints()
            holder.High:SetPoint("TOPRIGHT", holder, "BOTTOMRIGHT", 0, -8)
            holder.High:SetText(max)
        end
        slider:SetScript("OnValueChanged", function(self, v)
            local vv = math.floor(v + 0.5)
            if holder.Text then holder.Text:SetText(label .. ": " .. vv) end
            onChanged(vv)
        end)
        local function nudge(delta)
            local current = slider:GetValue() or value
            local nextVal = math.min(holder._max, math.max(holder._min, current + (delta * holder._step)))
            slider:SetValue(nextVal)
        end
        if holder.DecrementButton then holder.DecrementButton:SetScript("OnClick", function() nudge(-1) end) end
        if holder.IncrementButton then holder.IncrementButton:SetScript("OnClick", function() nudge(1) end) end
        if not holder.SetEnabled then
            holder.SetEnabled = function(self, enabled)
                if self.Slider then
                    if enabled then self.Slider:Enable() else self.Slider:Disable() end
                    self.Slider:EnableMouse(enabled)
                end
                if self.DecrementButton then
                    if enabled then self.DecrementButton:Enable() else self.DecrementButton:Disable() end
                end
                if self.IncrementButton then
                    if enabled then self.IncrementButton:Enable() else self.IncrementButton:Disable() end
                end
                self:SetAlpha(enabled and 1 or 0.4)
            end
        end
        if not holder.SetValue then
            holder.SetValue = function(self, val)
                if self.Slider then self.Slider:SetValue(val) end
            end
        end
        return holder
    end

    local legacy = CreateFrame("Slider", nil, parent, "OptionsSliderTemplate")
    legacy:SetFrameLevel((parent:GetFrameLevel() or 1) + 2)
    legacy:EnableMouse(true)
    legacy:SetMinMaxValues(min, max)
    legacy:SetValueStep(step)
    legacy:SetObeyStepOnDrag(true)
    legacy:SetValue(value)
    legacy._min, legacy._max, legacy._step = min, max, step
    if legacy.SetThumbTexture then
        legacy:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Horizontal")
    end
    if not legacy.Text then
        legacy.Text = legacy:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    end
    legacy.Text:ClearAllPoints()
    legacy.Text:SetPoint("BOTTOM", legacy, "TOP", 0, 2)
    legacy.Text:SetText(label .. ": " .. value)
    if legacy.Low and legacy.High then
        legacy.Low:ClearAllPoints()
        legacy.Low:SetPoint("TOPLEFT", legacy, "BOTTOMLEFT", 0, -8)
        legacy.Low:SetText(min)
        legacy.High:ClearAllPoints()
        legacy.High:SetPoint("TOPRIGHT", legacy, "BOTTOMRIGHT", 0, -8)
        legacy.High:SetText(max)
    end
    legacy:SetScript("OnValueChanged", function(self, v)
        local vv = math.floor(v + 0.5)
        if self.Text then self.Text:SetText(label .. ": " .. vv) end
        onChanged(vv)
    end)

    local function nudge(delta)
        local current = legacy:GetValue() or value
        local nextVal = math.min(legacy._max, math.max(legacy._min, current + (delta * legacy._step)))
        legacy:SetValue(nextVal)
    end
    local left = CreateFrame("Button", nil, legacy, "UIPanelButtonTemplate")
    left:SetSize(18, 18)
    left:SetText("<")
    left:SetPoint("RIGHT", legacy, "LEFT", -4, 0)
    left:SetScript("OnClick", function() nudge(-1) end)

    local right = CreateFrame("Button", nil, legacy, "UIPanelButtonTemplate")
    right:SetSize(18, 18)
    right:SetText(">")
    right:SetPoint("LEFT", legacy, "RIGHT", 4, 0)
    right:SetScript("OnClick", function() nudge(1) end)
    legacy._left = left
    legacy._right = right
    if not legacy.SetEnabled then
        legacy.SetEnabled = function(self, enabled)
            if enabled then self:Enable() else self:Disable() end
            self:EnableMouse(enabled)
            if self._left then if enabled then self._left:Enable() else self._left:Disable() end end
            if self._right then if enabled then self._right:Enable() else self._right:Disable() end end
            self:SetAlpha(enabled and 1 or 0.4)
        end
    end

    return legacy
end

local function dropdown(parent, width, current, items, onSelect)
    local dropdown
    if WOW_PROJECT_ID == WOW_PROJECT_MAINLINE then
        dropdown = CreateFrame("DropdownButton", nil, parent, "WowStyle1DropdownTemplate")
    end
    if dropdown and dropdown.SetupMenu then
        dropdown:SetSize(width, 22)
        local idx = 1
        for i, v in ipairs(items or {}) do
            if v == current then idx = i break end
        end
        local function setSelection(val, trigger)
            dropdown.currentValue = val
            dropdown:SetText(val or "")
            if trigger and onSelect then onSelect(val) end
        end
        dropdown:SetupMenu(function(self, root)
            for _, val in ipairs(items or {}) do
                root:CreateCheckbox(val, function() return dropdown.currentValue == val end, function()
                    setSelection(val, true)
                end)
            end
        end)
        setSelection(items and items[idx] or current, false)
        return dropdown
    end

    local drop = CreateFrame("Frame", nil, parent, "UIDropDownMenuTemplate")
    UIDropDownMenu_SetWidth(drop, width)
    UIDropDownMenu_SetText(drop, current)
    UIDropDownMenu_Initialize(drop, function()
        local info = UIDropDownMenu_CreateInfo()
        for _, item in ipairs(items or {}) do
            info.text = item
            info.func = function()
                UIDropDownMenu_SetText(drop, item)
                if onSelect then onSelect(item) end
            end
            info.checked = (item == current)
            UIDropDownMenu_AddButton(info)
        end
    end)
    return drop
end

local function createMainActionButton(parent, text, onClick)
    local button
    if WOW_PROJECT_ID == WOW_PROJECT_MAINLINE then
        local ok, modern = pcall(CreateFrame, "Button", nil, parent, "UIMenuButtonStretchTemplate")
        if ok and modern then
            button = modern
            button:SetHeight(24)
            if button.SetNormalFontObject then button:SetNormalFontObject("GameFontNormalSmall") end
            if button.SetHighlightFontObject then button:SetHighlightFontObject("GameFontHighlightSmall") end
            if button.SetDisabledFontObject then button:SetDisabledFontObject("GameFontDisableSmall") end
        end
    end
    if not button then
        button = CreateFrame("Button", nil, parent, "UIPanelButtonTemplate")
        button:SetHeight(22)
    end
    button:SetText(text or "")
    button:SetScript("OnClick", onClick)
    return button
end

if Controls then
    if Controls.createCheck then
        createCheck = Controls.createCheck
    end
    if Controls.createSlider then
        createSlider = Controls.createSlider
    end
    if Controls.createDropdown then
        dropdown = Controls.createDropdown
    end
    if Controls.createMainActionButton then
        createMainActionButton = Controls.createMainActionButton
    end
end

local function openColorPicker(currentColor, onChanged)
    local c = currentColor or { 1, 1, 1, 1 }
    local function pushColor(r, g, b, a)
        onChanged({ r or 1, g or 1, b or 1, a or 1 })
    end

    if Settings and Settings.OpenColorPicker then
        Settings.OpenColorPicker({
            r = c[1] or 1,
            g = c[2] or 1,
            b = c[3] or 1,
            opacity = 1 - (c[4] or 1),
            swatchFunc = function()
                if ColorPickerFrame and ColorPickerFrame.GetColorRGB then
                    local r, g, b = ColorPickerFrame:GetColorRGB()
                    local a = 1 - (ColorPickerFrame.opacity or 0)
                    pushColor(r, g, b, a)
                else
                    pushColor(c[1], c[2], c[3], c[4])
                end
            end,
            opacityFunc = function()
                if ColorPickerFrame and ColorPickerFrame.GetColorRGB then
                    local r, g, b = ColorPickerFrame:GetColorRGB()
                    local a = 1 - (ColorPickerFrame.opacity or 0)
                    pushColor(r, g, b, a)
                end
            end,
            cancelFunc = function()
                pushColor(c[1], c[2], c[3], c[4])
            end,
        })
        return
    end

    if not ColorPickerFrame and LoadAddOn then
        LoadAddOn("Blizzard_ColorPicker")
    end
    local picker = ColorPickerFrame
    if not picker then return end

    if picker.SetupColorPickerAndShow then
        picker:SetupColorPickerAndShow({
            r = c[1] or 1,
            g = c[2] or 1,
            b = c[3] or 1,
            opacity = 1 - (c[4] or 1),
            swatchFunc = function()
                local r, g, b = picker:GetColorRGB()
                local a = 1 - (picker.opacity or 0)
                pushColor(r, g, b, a)
            end,
            opacityFunc = function()
                local r, g, b = picker:GetColorRGB()
                local a = 1 - (picker.opacity or 0)
                pushColor(r, g, b, a)
            end,
            cancelFunc = function()
                pushColor(c[1], c[2], c[3], c[4])
            end,
            hasOpacity = true,
        })
        return
    end

    if not picker.SetColorRGB then return end
    picker.hasOpacity = true
    picker.opacity = 1 - (c[4] or 1)
    picker.func = function()
        local r, g, b = picker:GetColorRGB()
        local a = 1 - (picker.opacity or 0)
        pushColor(r, g, b, a)
    end
    picker.opacityFunc = picker.func
    picker.cancelFunc = function()
        pushColor(c[1], c[2], c[3], c[4])
    end
    picker:SetColorRGB(c[1] or 1, c[2] or 1, c[3] or 1)
    picker:Hide()
    picker:Show()
end

local function createColorSwatch(parent, label, initialColor, onChanged)
    local swatch = CreateFrame("Button", nil, parent)
    swatch:SetSize(26, 18)
    swatch:SetNormalTexture("Interface\\Buttons\\WHITE8X8")
    swatch:GetNormalTexture():SetVertexColor(unpack(initialColor or { 1, 1, 1, 1 }))
    swatch:SetHighlightTexture("Interface\\Buttons\\UI-Common-MouseHilight")
    swatch:GetHighlightTexture():SetBlendMode("ADD")
    swatch:SetScript("OnClick", function()
        openColorPicker(initialColor, function(color)
            initialColor = color
            if swatch:GetNormalTexture() then
                swatch:GetNormalTexture():SetVertexColor(color[1] or 1, color[2] or 1, color[3] or 1, color[4] or 1)
            end
            onChanged(color)
        end)
    end)
    swatch.SetColor = function(_, color)
        initialColor = color
        if swatch:GetNormalTexture() then
            swatch:GetNormalTexture():SetVertexColor(color[1] or 1, color[2] or 1, color[3] or 1, color[4] or 1)
        end
    end

    local labelText = parent:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    labelText:SetText(label)
    labelText:SetPoint("LEFT", swatch, "RIGHT", 8, 0)

    return swatch, labelText
end

function Options:Register(rescan)
    if self.panel then return self.panel end
    self.registered = true

    local function applyFiltersOrRescan()
        if ns.ApplyFilters and ns.ApplyFilters() then return end
        if type(rescan) == "function" then
            rescan(false)
        elseif ns.TriggerScan then
            ns.TriggerScan(false)
        end
    end

    local themeAlphaSlider
    local customPreview
    local swBg, swBorder, swText, swText2, swAccent, swAccent2
    local loginScan, toggleWithAch, remixAuto, positionMode
    local thresh, speedDrop
    local showTop, lockTop, clickTop, themeTop, alphaSlider
    local themeDrop
    local function isThemeAlphaAdjustable(themeKey)
        return themeKey ~= "Parchment" and themeKey ~= "Stone"
    end

    local function setDropdownValue(drop, val)
        if not drop then return end
        if drop.SetText then
            drop.currentValue = val
            drop:SetText(val or "")
            return
        end
        if UIDropDownMenu_SetText then
            UIDropDownMenu_SetText(drop, val)
        end
    end

    local function setSliderValue(slider, val)
        if not slider then return end
        if slider.SetValue then
            slider:SetValue(val)
        elseif slider.Slider and slider.Slider.SetValue then
            slider.Slider:SetValue(val)
        end
    end

    local panel = CreateFrame("Frame", "ACAU_OptionsPanel")
    panel.name = "Almost Completed Achievements"

    local title = panel:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge")
    title:SetPoint("TOPLEFT", panel, "TOPLEFT", 16, -16)
    title:SetText("Almost Completed Achievements")

    local function reopenCategory()
        if Settings and Settings.OpenToCategory then
            if Options.category and Options.category.ID then
                Settings.OpenToCategory(Options.category.ID)
                return
            end
            if Options.categoryName then
                Settings.OpenToCategory(Options.categoryName)
                return
            end
        end
        if InterfaceOptionsFrame_OpenToCategory then
            local target = Options.category or panel
            InterfaceOptionsFrame_OpenToCategory(target)
            InterfaceOptionsFrame_OpenToCategory(target)
        end
    end

    local function resetAll()
        ACAU_Config = Util:MergeDefaults({}, Const.DEFAULTS)
        ACAU_Global = Util:MergeDefaults({}, {
            manualScanOnly = Const.DEFAULTS.manualScanOnly,
            toggleWithAchievementFrame = Const.DEFAULTS.toggleWithAchievementFrame,
            positionAccountWide = true,
            panelPoint = nil,
            top5Point = nil,
            ignoreAll = {},
        })
        ACAU_CategoryDB = Util:MergeDefaults({}, Const.CATEGORY_DEFAULTS)
        ACAU_ExpFilterDB = Util:MergeDefaults({}, Const.EXPANSION_DEFAULTS)
        ACAU_Top5DB = Util:MergeDefaults({}, Const.DEFAULTS.top5)
        if ns.Panel and ns.Panel.ResetPosition then ns.Panel:ResetPosition() end
        if ns.Tracker and ns.Tracker.RefreshPosition then ns.Tracker:RefreshPosition() end
        if Remix and Remix.Disable then Remix:Disable() end

        if loginScan and loginScan.SetChecked then loginScan:SetChecked(not ACAU_Global.manualScanOnly) end
        if toggleWithAch and toggleWithAch.SetChecked then toggleWithAch:SetChecked(ACAU_Global.toggleWithAchievementFrame ~= false) end
        if remixAuto and remixAuto.SetChecked then remixAuto:SetChecked(ACAU_Config.remixAutoApply ~= false) end
        if positionMode and positionMode.SetChecked then positionMode:SetChecked(true) end

        if thresh then setSliderValue(thresh, ACAU_Config.threshold or Const.DEFAULTS.threshold) end
        if speedDrop then setDropdownValue(speedDrop, ACAU_Config.parseSpeed or Const.DEFAULTS.parseSpeed) end

        if showTop and showTop.SetChecked then showTop:SetChecked(ACAU_Top5DB.shown) end
        if lockTop and lockTop.SetChecked then lockTop:SetChecked(ACAU_Top5DB.locked) end
        if clickTop and clickTop.SetChecked then clickTop:SetChecked(ACAU_Top5DB.clickThrough) end
        if themeTop and themeTop.SetChecked then themeTop:SetChecked(ACAU_Top5DB.applyTheme ~= false) end
        if alphaSlider then setSliderValue(alphaSlider, math.floor((ACAU_Top5DB.alpha or Const.DEFAULTS.top5.alpha or 0.9) * 100)) end

        if themeDrop then
            setDropdownValue(themeDrop, ACAU_Config.theme or Const.DEFAULTS.theme)
            if customPreview then customPreview.previewThemeKey = ACAU_Config.theme end
        end
        if themeAlphaSlider then
            local defaultAlpha = math.floor(((Const.DEFAULTS.themeAlpha or 1) * 100) + 0.5)
            setSliderValue(themeAlphaSlider, defaultAlpha)
            if themeAlphaSlider.SetEnabled then themeAlphaSlider:SetEnabled(isThemeAlphaAdjustable(Const.DEFAULTS.theme or "Modern")) end
        end
        if swBg and swBg.Refresh then swBg:Refresh() end
        if swBorder and swBorder.Refresh then swBorder:Refresh() end
        if swText and swText.Refresh then swText:Refresh() end
        if swText2 and swText2.Refresh then swText2:Refresh() end
        if swAccent and swAccent.Refresh then swAccent:Refresh() end
        if swAccent2 and swAccent2.Refresh then swAccent2:Refresh() end
        if customPreview and customPreview.Refresh then customPreview:Refresh() end
        if ns.Tracker and ns.Tracker.SetAlpha then ns.Tracker:SetAlpha(ACAU_Top5DB.alpha) end
        if ns.Tracker and ns.Tracker.SetLocked then ns.Tracker:SetLocked(ACAU_Top5DB.locked) end
        if ns.Tracker and ns.Tracker.SetClickThrough then ns.Tracker:SetClickThrough(ACAU_Top5DB.clickThrough) end
        if ns.Tracker and ns.Tracker.ApplyTheme then ns.Tracker:ApplyTheme() end
        if ns.Tracker and ns.Tracker.frame then
            if ACAU_Top5DB.shown then ns.Tracker.frame:Show() else ns.Tracker.frame:Hide() end
        end
        if ns.Panel and ns.Panel.UpdateRemixUI then ns.Panel:UpdateRemixUI() end
        if ns.Panel and ns.Panel.RefreshFiltersUI then ns.Panel:RefreshFiltersUI() end
        if ns.Panel and ns.Panel.RefreshTheme then ns.Panel:RefreshTheme() end
        rescan(false)
        reopenCategory()
    end

    local resetAllBtn = createMainActionButton(panel, "Reset all", resetAll)
    resetAllBtn:SetSize(90, 24)
    resetAllBtn:SetPoint("TOPRIGHT", panel, "TOPRIGHT", -140, -16)

    local resetPosBtn = createMainActionButton(panel, "Reset Position", function()
        if ns.Panel and ns.Panel.ResetPosition then
            ns.Panel:ResetPosition()
        end
        reopenCategory()
    end)
    resetPosBtn:SetSize(110, 24)
    resetPosBtn:SetPoint("LEFT", resetAllBtn, "RIGHT", 6, 0)

    if type(ns.EnsureDevInfoModule) == "function" then
        local devInfoModule = ns.EnsureDevInfoModule()
        if devInfoModule and devInfoModule.AttachButton then
            panel.devInfoButton = devInfoModule:AttachButton(panel, {
                point = "RIGHT",
                relative_to = resetAllBtn,
                relative_point = "LEFT",
                x = -6,
                y = 0,
                width = 72,
                height = 24,
                frame_level_offset = 12,
                text = "Dev Info",
            })
        end
    end

    local desc = panel:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
    desc:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -6)
    desc:SetText("Configure scan rules, filters, and the Top 5 tracker.")

    local scroll = CreateFrame("ScrollFrame", nil, panel, "UIPanelScrollFrameTemplate")
    scroll:SetPoint("TOPLEFT", desc, "BOTTOMLEFT", -4, -8)
    scroll:SetPoint("BOTTOMRIGHT", panel, "BOTTOMRIGHT", -26, 16)

    local content = CreateFrame("Frame", nil, scroll)
    content:SetPoint("TOPLEFT", scroll, "TOPLEFT", 0, 0)
    content:SetPoint("RIGHT", scroll, "RIGHT", 0, 0)
    content:SetSize(820, 520)
    scroll:SetScrollChild(content)
    scroll:HookScript("OnSizeChanged", function(self, w)
        if content and w and w > 0 then
            content:SetWidth(w - 2)
        end
    end)
    initLegacyScrollFrame(scroll, 28)

    local function getCustomColor(key)
        local cfg = ACAU_Config.customTheme or Const.DEFAULTS.customTheme or {}
        local c = cfg[key] or (Const.DEFAULTS.customTheme and Const.DEFAULTS.customTheme[key]) or { 1, 1, 1, 1 }
        return { c[1] or 1, c[2] or 1, c[3] or 1, c[4] or 1 }
    end
    local function updateCustomColor(key, color)
        ACAU_Config.customTheme = ACAU_Config.customTheme or {}
        ACAU_Config.customTheme[key] = color
        if ns.Panel and ns.Panel.RefreshTheme then ns.Panel:RefreshTheme() end
        if customPreview and customPreview.Refresh then customPreview:Refresh() end
    end

    local function sectionFrame(label, x, yPos, width)
        local frame = CreateFrame("Frame", nil, content, "BackdropTemplate")
        frame:SetPoint("TOPLEFT", content, "TOPLEFT", x, yPos)
        frame:SetWidth(width)
        frame:SetBackdrop({
            bgFile = "Interface\\Buttons\\WHITE8X8",
            edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
            tile = false,
            edgeSize = 12,
            insets = { left = 4, right = 4, top = 4, bottom = 4 },
        })
        frame:SetBackdropColor(0.05, 0.05, 0.07, 0.9)
        frame:SetBackdropBorderColor(0.2, 0.2, 0.24, 0.9)

        frame.header = frame:CreateTexture(nil, "ARTWORK")
        frame.header:SetColorTexture(1, 1, 1, 0.04)
        frame.header:SetPoint("TOPLEFT", frame, "TOPLEFT", 4, -4)
        frame.header:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -4, -4)
        frame.header:SetHeight(24)

        frame.title = frame:CreateFontString(nil, "ARTWORK", "GameFontNormal")
        frame.title:SetPoint("TOPLEFT", frame, "TOPLEFT", 12, -10)
        frame.title:SetText(label)

        frame.pad = 12
        frame.innerY = -36
        return frame
    end

    local function finishSection(frame, extraBottom)
        local pad = extraBottom or 12
        local height = math.abs(frame.innerY) + pad
        frame:SetHeight(height)
        return height
    end

    local function addSubLabel(frame, text, yOffset)
        local label = frame:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
        label:SetPoint("TOPLEFT", frame, "TOPLEFT", frame.pad, yOffset or frame.innerY)
        label:SetText(text)
        frame.innerY = (yOffset or frame.innerY) - 20
        return label
    end

    local layoutPadding = 12
    local sectionGap = 3
    local contentWidth = 820
    local innerWidth = contentWidth - (layoutPadding * 2)
    local leftWidth = math.floor((innerWidth - sectionGap) * 0.42) - 35
    local rightWidth = leftWidth
    local leftX = layoutPadding
    local rightX = leftX + leftWidth + sectionGap
    local cursorY = -6
    local combinedWidth = leftWidth + rightWidth + sectionGap

    local generalSection = sectionFrame("General Options", leftX, cursorY, leftWidth)
    local top5Section = sectionFrame("Top 5 Options", rightX, cursorY, rightWidth)

    loginScan = createCheck(generalSection, "Scan on login (recommended)", not (ACAU_Global and ACAU_Global.manualScanOnly), function(val)
        ACAU_Global.manualScanOnly = not val
        if ns.Panel and ns.Panel.frame and ns.Panel.frame.progress and ns.Panel.frame.progress.Text then
            ns.Panel.frame.progress.Text:SetText(val and "Idle" or "Scan on login disabled; press Rescan")
        end
    end)
    loginScan:SetPoint("TOPLEFT", generalSection, "TOPLEFT", generalSection.pad, generalSection.innerY)
    generalSection.innerY = generalSection.innerY - 28

    toggleWithAch = createCheck(generalSection, "Toggle alongside Blizzard achievement panel", not (ACAU_Global and ACAU_Global.toggleWithAchievementFrame == false), function(val)
        ACAU_Global.toggleWithAchievementFrame = val
    end)
    toggleWithAch:SetPoint("TOPLEFT", generalSection, "TOPLEFT", generalSection.pad, generalSection.innerY)
    generalSection.innerY = generalSection.innerY - 28

    positionMode = createCheck(generalSection, "Account-wide window positions", not (ACAU_Global and ACAU_Global.positionAccountWide == false), function(val)
        ACAU_Global.positionAccountWide = not not val
        if val then
            if ACAU_Config and ACAU_Config.panelPoint then
                ACAU_Global.panelPoint = Util:DeepCopy(ACAU_Config.panelPoint)
            end
            if ACAU_Top5DB and ACAU_Top5DB.point then
                ACAU_Global.top5Point = Util:DeepCopy(ACAU_Top5DB.point)
            end
        else
            if ACAU_Global and ACAU_Global.panelPoint then
                ACAU_Config.panelPoint = Util:DeepCopy(ACAU_Global.panelPoint)
            end
            if ACAU_Global and ACAU_Global.top5Point then
                ACAU_Top5DB.point = Util:DeepCopy(ACAU_Global.top5Point)
            end
        end
        if ns.Panel and ns.Panel.RefreshAnchor then ns.Panel:RefreshAnchor() end
        if ns.Tracker and ns.Tracker.RefreshPosition then ns.Tracker:RefreshPosition() end
    end)
    positionMode:SetPoint("TOPLEFT", generalSection, "TOPLEFT", generalSection.pad, generalSection.innerY)
    generalSection.innerY = generalSection.innerY - 28

    remixAuto = createCheck(generalSection, "Enable Remix Mode on Legion Remix characters", ACAU_Config.remixAutoApply ~= false, function(val)
        ACAU_Config.remixAutoApply = val
    end)
    remixAuto:SetPoint("TOPLEFT", generalSection, "TOPLEFT", generalSection.pad, generalSection.innerY)
    generalSection.innerY = generalSection.innerY - 28

    thresh = createSlider(generalSection, "Completion threshold", 0, 99, 1, ACAU_Config.threshold or 80, function(v)
        ACAU_Config.threshold = v
        applyFiltersOrRescan()
    end)
    thresh:SetPoint("TOPLEFT", generalSection, "TOPLEFT", generalSection.pad - 4, generalSection.innerY - 6)
    local speedLabel = generalSection:CreateFontString(nil, "ARTWORK", "GameFontNormal")
    speedLabel:SetPoint("TOPLEFT", generalSection, "TOPLEFT", generalSection.pad, generalSection.innerY - 44)
    speedLabel:SetText("Scan speed")
    speedDrop = dropdown(generalSection, 120, ACAU_Config.parseSpeed or "Auto", { "Auto", "Fast", "Smooth", "Slow" }, function(val)
        ACAU_Config.parseSpeed = val
        if ns.ApplyFilters then ns.ApplyFilters() end
    end)
    speedDrop:SetPoint("TOPLEFT", speedLabel, "BOTTOMLEFT", -2, -4)
    generalSection.innerY = generalSection.innerY - 70

    showTop = createCheck(top5Section, "Show on login", ACAU_Top5DB.shown, function(val)
        ACAU_Top5DB.shown = val
        if ns.Tracker and ns.Tracker.frame then
            if val then ns.Tracker.frame:Show() else ns.Tracker.frame:Hide() end
        end
    end)
    showTop:SetPoint("TOPLEFT", top5Section, "TOPLEFT", top5Section.pad, top5Section.innerY)
    top5Section.innerY = top5Section.innerY - 24

    lockTop = createCheck(top5Section, "Lock position", ACAU_Top5DB.locked, function(val)
        ACAU_Top5DB.locked = val
        if ns.Tracker and ns.Tracker.SetLocked then ns.Tracker:SetLocked(val) end
    end)
    lockTop:SetPoint("TOPLEFT", top5Section, "TOPLEFT", top5Section.pad, top5Section.innerY)
    top5Section.innerY = top5Section.innerY - 24

    clickTop = createCheck(top5Section, "Allow click-through", ACAU_Top5DB.clickThrough, function(val)
        ACAU_Top5DB.clickThrough = val
        if ns.Tracker and ns.Tracker.SetClickThrough then ns.Tracker:SetClickThrough(val) end
    end)
    clickTop:SetPoint("TOPLEFT", top5Section, "TOPLEFT", top5Section.pad, top5Section.innerY)
    top5Section.innerY = top5Section.innerY - 24

    themeTop = createCheck(top5Section, "Apply theme to Top 5 window", ACAU_Top5DB.applyTheme ~= false, function(val)
        ACAU_Top5DB.applyTheme = val
        if ns.Tracker and ns.Tracker.ApplyTheme then ns.Tracker:ApplyTheme() end
    end)
    themeTop:SetPoint("TOPLEFT", top5Section, "TOPLEFT", top5Section.pad, top5Section.innerY)
    top5Section.innerY = top5Section.innerY - 34

    alphaSlider = createSlider(top5Section, "Tracker opacity", 20, 100, 1, math.floor((ACAU_Top5DB.alpha or 0.9) * 100), function(v)
        local alpha = v / 100
        ACAU_Top5DB.alpha = alpha
        if ns.Tracker and ns.Tracker.SetAlpha then ns.Tracker:SetAlpha(alpha) end
    end)
    alphaSlider:SetPoint("TOPLEFT", top5Section, "TOPLEFT", top5Section.pad - 10, top5Section.innerY - 8)
    top5Section.innerY = top5Section.innerY - 72

    local generalHeight = finishSection(generalSection)
    local top5Height = finishSection(top5Section)
    local rowHeight = math.max(generalHeight, top5Height)
    if generalHeight ~= rowHeight then generalSection:SetHeight(rowHeight) end
    if top5Height ~= rowHeight then top5Section:SetHeight(rowHeight) end
    cursorY = cursorY - rowHeight - sectionGap

    local themeSection = sectionFrame("Theme & Appearance", leftX, cursorY, combinedWidth)
    local currentTheme = ACAU_Config.theme or "Modern"
    if currentTheme == "Ocean" then currentTheme = "Sapphire"; ACAU_Config.theme = currentTheme end
    if currentTheme == "Sunset" then currentTheme = "Citrine"; ACAU_Config.theme = currentTheme end

    local themeLabel = themeSection:CreateFontString(nil, "ARTWORK", "GameFontNormal")
    themeLabel:SetPoint("TOPLEFT", themeSection, "TOPLEFT", themeSection.pad, themeSection.innerY)
    themeLabel:SetText("Theme")
    themeDrop = dropdown(themeSection, 180, currentTheme, { "Modern", "Parchment", "Stone", "Solarized", "Midnight", "Emerald", "Fel Remix", "Ruby", "Sapphire", "Citrine", "Amethyst", "Custom" }, function(val)
        ACAU_Config.theme = val
        if ns.Panel and ns.Panel.RefreshTheme then ns.Panel:RefreshTheme() end
        if themeAlphaSlider and themeAlphaSlider.SetEnabled then
            themeAlphaSlider:SetEnabled(isThemeAlphaAdjustable(val))
        end
        if customPreview then
            customPreview.previewThemeKey = val
            if customPreview.Refresh then customPreview:Refresh() end
        end
    end)
    themeDrop:SetPoint("TOPLEFT", themeLabel, "BOTTOMLEFT", 4, -4)

    local alphaPercent = math.floor(((ACAU_Config.themeAlpha or Const.DEFAULTS.themeAlpha or 1) * 100) + 0.5)
    themeAlphaSlider = createSlider(themeSection, "Theme opacity", 30, 100, 1, alphaPercent, function(v)
        ACAU_Config.themeAlpha = v / 100
        if ns.Panel and ns.Panel.RefreshTheme then ns.Panel:RefreshTheme() end
        if customPreview and customPreview.Refresh then customPreview:Refresh() end
    end)
    themeAlphaSlider:SetPoint("TOPRIGHT", themeSection, "TOPRIGHT", -20, themeSection.innerY - 18)
    if themeAlphaSlider.SetEnabled then
        themeAlphaSlider:SetEnabled(isThemeAlphaAdjustable(ACAU_Config.theme or "Modern"))
    end
    themeSection.innerY = themeSection.innerY - 70

    local function applyCustomPreview(frame)
        if not frame then return end
        local key = frame.previewThemeKey or "Custom"
        local style = nil
        if ns.Panel and ns.Panel.GetThemeStyle then
            style = ns.Panel:GetThemeStyle(key)
        end
        if not style or key == "Custom" then
            style = style or {}
            style.bgFile = style.bgFile or "Interface\\Buttons\\WHITE8X8"
            style.edgeFile = style.edgeFile or "Interface\\Tooltips\\UI-Tooltip-Border"
            style.bgColor = style.bgColor or getCustomColor("bgColor")
            style.borderColor = style.borderColor or getCustomColor("borderColor")
            style.palette = style.palette or {
                textPrimary = getCustomColor("textPrimary"),
                textSecondary = getCustomColor("textSecondary"),
                accent = getCustomColor("accent"),
                accentAlt = getCustomColor("accentAlt"),
                highlight = { 1, 1, 1, 0.22 },
            }
        end
        local alphaMult = 1
        if key ~= "Parchment" and key ~= "Stone" then
            alphaMult = ((ACAU_Config and ACAU_Config.themeAlpha) or Const.DEFAULTS.themeAlpha or 1)
        end
        local bg = style.bgColor or { 0, 0, 0, 0.9 }
        local border = style.borderColor or { 1, 1, 1, 1 }
        local tile = style.tile ~= false
        frame:SetBackdrop({
            bgFile = style.bgFile or "Interface\\Buttons\\WHITE8X8",
            edgeFile = style.edgeFile or "Interface\\Tooltips\\UI-Tooltip-Border",
            tile = tile, tileSize = 8, edgeSize = 12,
            insets = { left = 3, right = 3, top = 3, bottom = 3 },
        })
        frame:SetBackdropColor(bg[1] or 0, bg[2] or 0, bg[3] or 0, (bg[4] or 1) * alphaMult)
        frame:SetBackdropBorderColor(border[1] or 1, border[2] or 1, border[3] or 1, (border[4] or 1) * alphaMult)
        if frame.title then
            local tp = style.palette and style.palette.textPrimary
            frame.title:SetTextColor(tp and tp[1] or 1, tp and tp[2] or 0.82, tp and tp[3] or 0)
        end
        if frame.subtitle then
            local ts = style.palette and style.palette.textSecondary
            frame.subtitle:SetTextColor(ts and ts[1] or 0.9, ts and ts[2] or 0.9, ts and ts[3] or 0.9)
        end
        if frame.points then
            local alt = style.palette and style.palette.accentAlt
            frame.points:SetTextColor(alt and alt[1] or 1, alt and alt[2] or 0.78, alt and alt[3] or 0.2)
        end
        if frame.accentBar then
            local acc = style.palette and style.palette.accent
            frame.accentBar:SetColorTexture(acc and acc[1] or 1, acc and acc[2] or 0.78, acc and acc[3] or 0.2, 0.45)
        end
    end

    addSubLabel(themeSection, "Theme creator", themeSection.innerY)
    local creatorStartY = themeSection.innerY
    local previewX = themeSection.pad + math.floor((themeSection:GetWidth() - (themeSection.pad * 2)) * 0.58)

    local previewLabel = themeSection:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    previewLabel:SetPoint("TOPLEFT", themeSection, "TOPLEFT", previewX, creatorStartY + 20)
    previewLabel:SetText("Preview")

    customPreview = CreateFrame("Frame", nil, themeSection, "BackdropTemplate")
    customPreview:SetSize(200, 96)
    customPreview:SetPoint("TOPLEFT", themeSection, "TOPLEFT", previewX, creatorStartY)
    customPreview.title = customPreview:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    customPreview.title:SetPoint("TOPLEFT", customPreview, "TOPLEFT", 10, -8)
    customPreview.title:SetText("Titles")
    customPreview.subtitle = customPreview:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    customPreview.subtitle:SetPoint("TOPLEFT", customPreview.title, "BOTTOMLEFT", 0, -6)
    customPreview.subtitle:SetText("Secondary text")
    customPreview.points = customPreview:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    customPreview.points:SetPoint("TOPLEFT", customPreview.subtitle, "BOTTOMLEFT", 0, -4)
    customPreview.points:SetText("Points")
    customPreview.accentBar = customPreview:CreateTexture(nil, "ARTWORK")
    customPreview.accentBar:SetPoint("BOTTOMLEFT", customPreview, "BOTTOMLEFT", 10, 10)
    customPreview.accentBar:SetPoint("BOTTOMRIGHT", customPreview, "BOTTOMRIGHT", -10, 10)
    customPreview.accentBar:SetHeight(10)
    customPreview.Refresh = function(self)
        applyCustomPreview(self)
    end

    local swatchYOffset = 0
    local function addSwatch(label, key)
        local swatch = nil
        swatch, _ = createColorSwatch(themeSection, label, getCustomColor(key), function(color)
            updateCustomColor(key, color)
            if customPreview and customPreview.Refresh then customPreview:Refresh() end
        end)
        swatch:SetPoint("TOPLEFT", themeSection, "TOPLEFT", themeSection.pad, creatorStartY + swatchYOffset)
        swatchYOffset = swatchYOffset - 26
        swatch.Refresh = function(self)
            if self.SetColor then self:SetColor(getCustomColor(key)) end
        end
        return swatch
    end

    swBg = addSwatch("Background", "bgColor")
    swBorder = addSwatch("Border", "borderColor")
    swText = addSwatch("Text primary", "textPrimary")
    swText2 = addSwatch("Text secondary", "textSecondary")
    swAccent = addSwatch("Accent", "accent")
    swAccent2 = addSwatch("Accent alt", "accentAlt")

    if customPreview and customPreview.Refresh then customPreview:Refresh() end

    themeSection.innerY = creatorStartY + swatchYOffset - 26
    finishSection(themeSection)

    cursorY = cursorY - themeSection:GetHeight() - sectionGap

    content:SetHeight(math.abs(cursorY) + 24)

    Options.panel = panel
    Options.categoryName = panel.name

    local launcher = ns.KagrokSharedLauncher
    if type(launcher) == "table" and type(launcher.RegisterSettingsPage) == "function" then
        if not Options.settingsRegistered then
            launcher:RegisterSettingsPage({
                id = ADDON_NAME,
                addon_name = ADDON_NAME,
                name = panel.name,
                get_panel = function()
                    return Options.panel or panel
                end,
                on_registered = function(category)
                    Options.category = category
                    Options.categoryName = panel.name
                end,
            })
            Options.settingsRegistered = true
        end
    elseif Settings and Settings.RegisterCanvasLayoutCategory then
        local category = Settings.RegisterCanvasLayoutCategory(panel, panel.name)
        Settings.RegisterAddOnCategory(category)
        Options.category = category
    else
        InterfaceOptions_AddCategory(panel)
        Options.category = panel
    end

    return panel
end
