--------------------------------------------------------------------------------
-- Nanami-UI: Setup Wizard  (SetupWizard.lua)
-- First-run guided configuration & re-run from ConfigUI
--------------------------------------------------------------------------------

SFrames.SetupWizard = {}
local SW = SFrames.SetupWizard

--------------------------------------------------------------------------------
-- Local state
--------------------------------------------------------------------------------
local overlay, panel, contentScroll, contentChild
local headerTitle, stepLabel
local btnPrev, btnNext, btnSkip
local stepDots = {}
local stepPages = {}
local stepBuilders = {}
local stepList = {}
local currentStep = 1
local runMode = "firstrun"
local completeCb = nil
local choices = {}
local built = false

local PANEL_W, PANEL_H = 620, 470
local CONTENT_W = PANEL_W - 40
local CONTENT_H = PANEL_H - 110

local wid = 0
local function WN(p) wid = wid + 1; return "NanamiWiz" .. p .. wid end

local function Clamp(v, lo, hi)
    if v < lo then return lo end
    if v > hi then return hi end
    return v
end

local function T() return SFrames.ActiveTheme end

--------------------------------------------------------------------------------
-- Lightweight widget helpers (self-contained, mirror ConfigUI style)
--------------------------------------------------------------------------------

local function HideTex(tex)
    if not tex then return end
    if tex.SetTexture then tex:SetTexture(nil) end
    tex:Hide()
end

-- Styled button (matches ConfigUI StyleButton)
local function MakeButton(parent, text, w, h, onClick)
    local btn = CreateFrame("Button", WN("Btn"), parent, "UIPanelButtonTemplate")
    btn:SetWidth(w); btn:SetHeight(h)
    btn:SetText(text)
    btn:SetScript("OnClick", onClick)

    local t = T()
    btn:SetBackdrop({
        bgFile   = "Interface\\Tooltips\\UI-Tooltip-Background",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true, tileSize = 16, edgeSize = 14,
        insets = { left = 3, right = 3, top = 3, bottom = 3 },
    })
    HideTex(btn:GetNormalTexture())
    HideTex(btn:GetPushedTexture())
    HideTex(btn:GetHighlightTexture())
    HideTex(btn:GetDisabledTexture())
    local nm = btn:GetName() or ""
    for _, suf in ipairs({"Left","Right","Middle"}) do
        local tx = _G[nm..suf]; if tx then tx:SetAlpha(0); tx:Hide() end
    end

    local function SetVis(state)
        local bg, bd, tx = t.buttonBg, t.buttonBorder, t.buttonText
        if btn.sfActive then
            bg, bd, tx = t.buttonActiveBg, t.buttonActiveBorder, t.buttonActiveText
        elseif state == "hover" then
            bg = t.buttonHoverBg; bd = t.btnHoverBd or t.buttonActiveBorder; tx = t.buttonActiveText
        elseif state == "down" then bg = t.buttonDownBg
        elseif state == "disabled" then bg = t.buttonDisabledBg; tx = t.buttonDisabledText end
        if btn.SetBackdropColor then btn:SetBackdropColor(bg[1],bg[2],bg[3],bg[4]) end
        if btn.SetBackdropBorderColor then btn:SetBackdropBorderColor(bd[1],bd[2],bd[3],bd[4]) end
        local fs = btn:GetFontString(); if fs then fs:SetTextColor(tx[1],tx[2],tx[3]) end
    end
    btn.RefreshVisual = function()
        if btn.sfActive then SetVis("active")
        elseif btn.IsEnabled and not btn:IsEnabled() then SetVis("disabled")
        else SetVis("normal") end
    end
    btn:SetScript("OnEnter", function() if this.IsEnabled and this:IsEnabled() and not this.sfActive then SetVis("hover") end end)
    btn:SetScript("OnLeave", function() if this.IsEnabled and this:IsEnabled() and not this.sfActive then SetVis("normal") end end)
    btn:SetScript("OnMouseDown", function() if this.IsEnabled and this:IsEnabled() and not this.sfActive then SetVis("down") end end)
    btn:SetScript("OnMouseUp",   function() if this.IsEnabled and this:IsEnabled() and not this.sfActive then SetVis("hover") end end)
    btn:RefreshVisual()
    return btn
end

local function MakeCheck(parent, label, x, y, getter, setter, onChange)
    local name = WN("Chk")
    local cb = CreateFrame("CheckButton", name, parent, "UICheckButtonTemplate")
    cb:SetWidth(18); cb:SetHeight(18)
    cb:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y)

    HideTex(cb:GetNormalTexture()); HideTex(cb:GetPushedTexture())
    HideTex(cb:GetHighlightTexture()); HideTex(cb:GetDisabledTexture())
    if cb.SetCheckedTexture then cb:SetCheckedTexture("") end
    if cb.SetDisabledCheckedTexture then cb:SetDisabledCheckedTexture("") end

    local t = T()
    local box = CreateFrame("Frame", nil, cb)
    box:SetPoint("TOPLEFT", cb, "TOPLEFT", 0, 0)
    box:SetPoint("BOTTOMRIGHT", cb, "BOTTOMRIGHT", 0, 0)
    box:SetFrameLevel(cb:GetFrameLevel()+1)
    box:SetBackdrop({
        bgFile   = "Interface\\Tooltips\\UI-Tooltip-Background",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true, tileSize = 16, edgeSize = 10,
        insets = { left = 2, right = 2, top = 2, bottom = 2 },
    })

    local function ApplyVis(checked)
        if checked then
            box:SetBackdropColor(t.checkFill[1], t.checkFill[2], t.checkFill[3], 0.88)
            box:SetBackdropBorderColor(t.checkFill[1], t.checkFill[2], t.checkFill[3], 1)
        else
            box:SetBackdropColor(t.checkBg[1], t.checkBg[2], t.checkBg[3], t.checkBg[4])
            box:SetBackdropBorderColor(t.checkBorder[1], t.checkBorder[2], t.checkBorder[3], t.checkBorder[4])
        end
    end

    local txt = _G[name.."Text"]
    if txt then
        txt:ClearAllPoints()
        txt:SetPoint("LEFT", cb, "RIGHT", 4, 0)
        txt:SetFont(SFrames:GetFont(), 11, "OUTLINE")
        txt:SetText(label)
        txt:SetTextColor(t.text[1], t.text[2], t.text[3])
    end

    cb:SetScript("OnEnter", function()
        if not this._chk then
            box:SetBackdropBorderColor(t.checkHoverBorder[1], t.checkHoverBorder[2], t.checkHoverBorder[3], t.checkHoverBorder[4])
        end
    end)
    cb:SetScript("OnLeave", function()
        if not this._chk then
            box:SetBackdropBorderColor(t.checkBorder[1], t.checkBorder[2], t.checkBorder[3], t.checkBorder[4])
        end
    end)
    cb:SetScript("OnClick", function()
        local v = (this:GetChecked() == 1 or this:GetChecked() == true) and true or false
        this._chk = v; ApplyVis(v)
        setter(v)
        if onChange then onChange(v) end
        PlaySound(v and "igMainMenuOptionCheckBoxOn" or "igMainMenuOptionCheckBoxOff")
    end)
    cb.Refresh = function()
        local v = getter() and true or false
        cb:SetChecked(v and 1 or 0); cb._chk = v; ApplyVis(v)
    end
    cb:Refresh()
    return cb
end

local function MakeSlider(parent, label, x, y, w, lo, hi, step, getter, setter, fmt)
    local sn = WN("Sld")
    local s = CreateFrame("Slider", sn, parent, "OptionsSliderTemplate")
    s:SetWidth(w); s:SetHeight(20)
    s:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y)
    s:SetMinMaxValues(lo, hi); s:SetValueStep(step)
    if s.SetObeyStepOnDrag then s:SetObeyStepOnDrag(true) end

    local low  = _G[sn.."Low"];  if low  then low:SetText(tostring(lo))  end
    local high = _G[sn.."High"]; if high then high:SetText(tostring(hi)) end
    local text = _G[sn.."Text"]

    local t = T()
    -- style track
    local regions = { s:GetRegions() }
    for i = 1, table.getn(regions) do
        local r = regions[i]
        if r and r.GetObjectType and r:GetObjectType() == "Texture" then r:SetTexture(nil) end
    end
    local track = s:CreateTexture(nil, "BACKGROUND")
    track:SetTexture("Interface\\Buttons\\WHITE8X8")
    track:SetPoint("LEFT", s, "LEFT", 0, 0); track:SetPoint("RIGHT", s, "RIGHT", 0, 0)
    track:SetHeight(4)
    track:SetVertexColor(t.sliderTrack[1], t.sliderTrack[2], t.sliderTrack[3], t.sliderTrack[4])
    local fill = s:CreateTexture(nil, "ARTWORK")
    fill:SetTexture("Interface\\Buttons\\WHITE8X8")
    fill:SetPoint("LEFT", track, "LEFT", 0, 0)
    fill:SetPoint("TOP", track, "TOP", 0, 0); fill:SetPoint("BOTTOM", track, "BOTTOM", 0, 0)
    fill:SetWidth(1)
    fill:SetVertexColor(t.sliderFill[1], t.sliderFill[2], t.sliderFill[3], t.sliderFill[4])
    if s.SetThumbTexture then s:SetThumbTexture("Interface\\Buttons\\WHITE8X8") end
    local thumb = s.GetThumbTexture and s:GetThumbTexture()
    if thumb then thumb:SetWidth(8); thumb:SetHeight(14)
        thumb:SetVertexColor(t.sliderThumb[1], t.sliderThumb[2], t.sliderThumb[3], t.sliderThumb[4])
    end

    local block = false
    local function UpdateFill()
        local mn, mx = s:GetMinMaxValues(); local v = s:GetValue() or mn
        local pct = 0; if mx > mn then pct = Clamp((v-mn)/(mx-mn), 0, 1) end
        local pw = math.floor((s:GetWidth() or 1) * pct + 0.5); if pw < 1 then pw = 1 end
        fill:SetWidth(pw)
    end
    local function UpdateLabel(v)
        local d = fmt and fmt(v) or v
        if text then text:SetText(label..": "..tostring(d)) end
    end
    s:SetScript("OnValueChanged", function()
        if block then return end
        local raw = this:GetValue() or lo
        local v; if step >= 1 then v = math.floor(raw+0.5) else v = math.floor(raw/step+0.5)*step end
        v = Clamp(v, lo, hi); UpdateLabel(v); UpdateFill(); setter(v)
    end)
    s.Refresh = function()
        local v = Clamp(tonumber(getter()) or lo, lo, hi)
        block = true; s:SetValue(v); block = false; UpdateLabel(v); UpdateFill()
    end
    if low  then low:SetTextColor(t.dimText[1], t.dimText[2], t.dimText[3])
        low:ClearAllPoints(); low:SetPoint("TOPLEFT", s, "BOTTOMLEFT", 0, 0) end
    if high then high:SetTextColor(t.dimText[1], t.dimText[2], t.dimText[3])
        high:ClearAllPoints(); high:SetPoint("TOPRIGHT", s, "BOTTOMRIGHT", 0, 0) end
    if text then text:SetTextColor(t.text[1], t.text[2], t.text[3])
        text:ClearAllPoints(); text:SetPoint("BOTTOM", s, "TOP", 0, 2) end
    s:Refresh()
    return s
end

local function MakeLabel(parent, text, x, y, size, r, g, b)
    local fs = parent:CreateFontString(nil, "OVERLAY")
    fs:SetFont(SFrames:GetFont(), size or 11, "OUTLINE")
    fs:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y)
    fs:SetText(text); fs:SetTextColor(r or 1, g or 1, b or 1)
    return fs
end

local function MakeDesc(parent, text, x, y, maxW)
    local fs = parent:CreateFontString(nil, "OVERLAY")
    fs:SetFont(SFrames:GetFont(), 9, "OUTLINE")
    fs:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y)
    if maxW then fs:SetWidth(maxW); fs:SetJustifyH("LEFT") end
    fs:SetText(text); fs:SetTextColor(0.65, 0.58, 0.62)
    return fs
end

local function MakeSection(parent, title, x, y, w, h, iconKey)
    local sec = CreateFrame("Frame", WN("Sec"), parent)
    sec:SetWidth(w); sec:SetHeight(h)
    sec:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y)
    sec:SetBackdrop({
        bgFile   = "Interface\\Tooltips\\UI-Tooltip-Background",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true, tileSize = 16, edgeSize = 14,
        insets = { left = 3, right = 3, top = 3, bottom = 3 },
    })
    local t = T()
    sec:SetBackdropColor(t.sectionBg[1], t.sectionBg[2], t.sectionBg[3], t.sectionBg[4])
    sec:SetBackdropBorderColor(t.sectionBorder[1], t.sectionBorder[2], t.sectionBorder[3], t.sectionBorder[4])

    local anchorX = 10
    if iconKey and SFrames and SFrames.CreateIcon then
        local ico = SFrames:CreateIcon(sec, iconKey, 12)
        ico:SetDrawLayer("OVERLAY")
        ico:SetPoint("TOPLEFT", sec, "TOPLEFT", 10, -9)
        ico:SetVertexColor(t.title[1], t.title[2], t.title[3])
        anchorX = 26
    end

    local tfs = sec:CreateFontString(nil, "OVERLAY")
    tfs:SetFont(SFrames:GetFont(), 11, "OUTLINE")
    tfs:SetPoint("TOPLEFT", sec, "TOPLEFT", anchorX, -9)
    tfs:SetText(title); tfs:SetTextColor(t.title[1], t.title[2], t.title[3])
    local div = sec:CreateTexture(nil, "ARTWORK")
    div:SetTexture("Interface\\Buttons\\WHITE8X8"); div:SetHeight(1)
    div:SetPoint("TOPLEFT", sec, "TOPLEFT", 8, -24)
    div:SetPoint("TOPRIGHT", sec, "TOPRIGHT", -8, -24)
    div:SetVertexColor(t.sectionBorder[1], t.sectionBorder[2], t.sectionBorder[3], 0.6)
    return sec
end

-- Toggle-group: row of small buttons, one active at a time
local function MakeBtnGroup(parent, x, y, opts, getter, setter)
    local btns = {}
    local bx = x
    for _, o in ipairs(opts) do
        local b = MakeButton(parent, o.label, o.w or 60, 22, nil)
        b:ClearAllPoints(); b:SetPoint("TOPLEFT", parent, "TOPLEFT", bx, y)
        b.key = o.key
        b:SetScript("OnClick", function()
            setter(this.key)
            for _, bb in ipairs(btns) do bb.sfActive = (bb.key == this.key); bb:RefreshVisual() end
            PlaySound("igMainMenuOptionCheckBoxOn")
        end)
        table.insert(btns, b); bx = bx + (o.w or 60) + 4
    end
    local grp = {}
    grp.Refresh = function()
        local v = getter()
        for _, bb in ipairs(btns) do bb.sfActive = (bb.key == v); bb:RefreshVisual() end
    end
    grp.Refresh()
    return grp
end

--------------------------------------------------------------------------------
-- Default / current choices
--------------------------------------------------------------------------------

local function GetDefaultChoices()
    return {
        themePreset      = "pink",
        useClassTheme    = false,
        enableMerchant   = true,
        enableQuestUI    = true,
        enableQuestLogSkin = true,
        enableTrainer    = true,
        enableSpellBook  = true,
        enableTradeSkill = true,
        charPanelEnable  = true,
        enableMail       = true,
        enablePetStable  = true,
        enableSocial     = true,
        enableInspect    = true,
        enableFlightMap  = true,
        enableChat       = true,
        translateEnabled = false,
        enableUnitFrames = true,
        enableActionBars = true,
        showPetBar       = true,
        showStanceBar    = true,
        showRightBars    = true,
        buttonRounded    = false,
        buttonInnerShadow = false,
        minimapEnabled   = true,
        minimapShowClock = true,
        minimapShowCoords = true,
        minimapMapStyle  = "auto",
        minimapMapShape  = "circle",
        buffEnabled      = true,
        buffIconSize     = 30,
        buffIconsPerRow  = 8,
        buffGrowDir      = "LEFT",
        buffShowTimer    = true,
        afkEnabled       = true,
        afkDelay         = 5,
        afkOutsideRest   = false,
        mapRevealEnabled = true,
        mapRevealAlpha   = 0.7,
        worldMapEnabled  = true,
        hcGlobalDisable  = false,
        iconSet          = "icon",
    }
end

local function GetCurrentChoices()
    local c = GetDefaultChoices()
    if not SFramesDB then return c end
    local db = SFramesDB
    if db.Theme then
        if db.Theme.preset       ~= nil then c.themePreset   = db.Theme.preset end
        if db.Theme.useClassTheme ~= nil then c.useClassTheme = db.Theme.useClassTheme end
        if db.Theme.iconSet      ~= nil then c.iconSet       = db.Theme.iconSet end
    end
    if db.enableMerchant   ~= nil then c.enableMerchant   = db.enableMerchant end
    if db.enableQuestUI    ~= nil then c.enableQuestUI    = db.enableQuestUI end
    if db.enableQuestLogSkin ~= nil then c.enableQuestLogSkin = db.enableQuestLogSkin end
    if db.enableTrainer    ~= nil then c.enableTrainer    = db.enableTrainer end
    if db.enableSpellBook  ~= nil then c.enableSpellBook  = db.enableSpellBook end
    if db.enableTradeSkill ~= nil then c.enableTradeSkill = db.enableTradeSkill end
    if db.charPanelEnable  ~= nil then c.charPanelEnable  = db.charPanelEnable end
    if db.enableMail       ~= nil then c.enableMail       = db.enableMail end
    if db.enablePetStable  ~= nil then c.enablePetStable  = db.enablePetStable end
    if db.enableSocial     ~= nil then c.enableSocial     = db.enableSocial end
    if db.enableInspect    ~= nil then c.enableInspect    = db.enableInspect end
    if db.enableFlightMap  ~= nil then c.enableFlightMap  = db.enableFlightMap end
    if db.enableChat       ~= nil then c.enableChat       = db.enableChat end
    if db.enableUnitFrames ~= nil then c.enableUnitFrames = db.enableUnitFrames end
    if db.afkEnabled       ~= nil then c.afkEnabled       = db.afkEnabled end
    if type(db.afkDelay)   == "number" then c.afkDelay    = db.afkDelay end
    if db.afkOutsideRest   ~= nil then c.afkOutsideRest   = db.afkOutsideRest end
    if db.Chat and db.Chat.translateEnabled ~= nil then c.translateEnabled = db.Chat.translateEnabled end
    if db.Chat and db.Chat.hcGlobalDisable  ~= nil then c.hcGlobalDisable  = db.Chat.hcGlobalDisable end
    if db.ActionBars then
        if db.ActionBars.enable        ~= nil then c.enableActionBars  = db.ActionBars.enable end
        if db.ActionBars.showPetBar    ~= nil then c.showPetBar        = db.ActionBars.showPetBar end
        if db.ActionBars.showStanceBar ~= nil then c.showStanceBar     = db.ActionBars.showStanceBar end
        if db.ActionBars.showRightBars ~= nil then c.showRightBars     = db.ActionBars.showRightBars end
        if db.ActionBars.buttonRounded ~= nil then c.buttonRounded     = db.ActionBars.buttonRounded end
        if db.ActionBars.buttonInnerShadow ~= nil then c.buttonInnerShadow = db.ActionBars.buttonInnerShadow end
    end
    if db.Minimap then
        if db.Minimap.enabled    ~= nil then c.minimapEnabled    = db.Minimap.enabled end
        if db.Minimap.showClock  ~= nil then c.minimapShowClock  = db.Minimap.showClock end
        if db.Minimap.showCoords ~= nil then c.minimapShowCoords = db.Minimap.showCoords end
        if db.Minimap.mapStyle   ~= nil then c.minimapMapStyle   = db.Minimap.mapStyle end
        if db.Minimap.mapShape   ~= nil then c.minimapMapShape   = db.Minimap.mapShape end
    end
    if db.MinimapBuffs then
        if db.MinimapBuffs.enabled       ~= nil then c.buffEnabled     = db.MinimapBuffs.enabled end
        if type(db.MinimapBuffs.iconSize)    == "number" then c.buffIconSize    = db.MinimapBuffs.iconSize end
        if type(db.MinimapBuffs.iconsPerRow) == "number" then c.buffIconsPerRow = db.MinimapBuffs.iconsPerRow end
        if db.MinimapBuffs.growDirection ~= nil then c.buffGrowDir     = db.MinimapBuffs.growDirection end
        if db.MinimapBuffs.showTimer     ~= nil then c.buffShowTimer   = db.MinimapBuffs.showTimer end
    end
    if db.MapReveal then
        if db.MapReveal.enabled ~= nil then c.mapRevealEnabled = db.MapReveal.enabled end
        if type(db.MapReveal.unexploredAlpha) == "number" then c.mapRevealAlpha = db.MapReveal.unexploredAlpha end
    end
    if db.WorldMap then
        if db.WorldMap.enabled ~= nil then c.worldMapEnabled = db.WorldMap.enabled end
    end
    return c
end

--------------------------------------------------------------------------------
-- Apply choices → SFramesDB
--------------------------------------------------------------------------------

local function ApplyChoices()
    if not SFramesDB then SFramesDB = {} end
    local c = choices

    if type(SFramesDB.Theme) ~= "table" then SFramesDB.Theme = {} end
    SFramesDB.Theme.preset       = c.themePreset
    SFramesDB.Theme.useClassTheme = c.useClassTheme
    SFramesDB.Theme.iconSet      = c.iconSet or "icon"

    SFramesDB.enableMerchant   = c.enableMerchant
    SFramesDB.enableQuestUI    = c.enableQuestUI
    SFramesDB.enableQuestLogSkin = c.enableQuestLogSkin
    SFramesDB.enableTrainer    = c.enableTrainer
    SFramesDB.enableSpellBook  = c.enableSpellBook
    SFramesDB.enableTradeSkill = c.enableTradeSkill
    SFramesDB.charPanelEnable  = c.charPanelEnable
    SFramesDB.enableMail       = c.enableMail
    SFramesDB.enablePetStable  = c.enablePetStable
    SFramesDB.enableSocial     = c.enableSocial
    SFramesDB.enableInspect    = c.enableInspect
    SFramesDB.enableFlightMap  = c.enableFlightMap

    SFramesDB.enableChat       = c.enableChat
    if type(SFramesDB.Chat) ~= "table" then SFramesDB.Chat = {} end
    SFramesDB.Chat.translateEnabled = c.translateEnabled
    SFramesDB.Chat.hcGlobalDisable  = c.hcGlobalDisable

    SFramesDB.enableUnitFrames = c.enableUnitFrames

    if type(SFramesDB.ActionBars) ~= "table" then SFramesDB.ActionBars = {} end
    SFramesDB.ActionBars.enable          = c.enableActionBars
    SFramesDB.ActionBars.showPetBar      = c.showPetBar
    SFramesDB.ActionBars.showStanceBar   = c.showStanceBar
    SFramesDB.ActionBars.showRightBars   = c.showRightBars
    SFramesDB.ActionBars.buttonRounded   = c.buttonRounded
    SFramesDB.ActionBars.buttonInnerShadow = c.buttonInnerShadow

    if type(SFramesDB.Minimap) ~= "table" then SFramesDB.Minimap = {} end
    SFramesDB.Minimap.enabled    = c.minimapEnabled
    SFramesDB.Minimap.showClock  = c.minimapShowClock
    SFramesDB.Minimap.showCoords = c.minimapShowCoords
    SFramesDB.Minimap.mapStyle   = c.minimapMapStyle
    SFramesDB.Minimap.mapShape   = c.minimapMapShape

    if type(SFramesDB.MinimapBuffs) ~= "table" then SFramesDB.MinimapBuffs = {} end
    SFramesDB.MinimapBuffs.enabled       = c.buffEnabled
    SFramesDB.MinimapBuffs.iconSize      = c.buffIconSize
    SFramesDB.MinimapBuffs.iconsPerRow   = c.buffIconsPerRow
    SFramesDB.MinimapBuffs.growDirection = c.buffGrowDir
    SFramesDB.MinimapBuffs.showTimer     = c.buffShowTimer

    SFramesDB.afkEnabled     = c.afkEnabled
    SFramesDB.afkDelay       = c.afkDelay
    SFramesDB.afkOutsideRest = c.afkOutsideRest

    if type(SFramesDB.MapReveal) ~= "table" then SFramesDB.MapReveal = {} end
    SFramesDB.MapReveal.enabled         = c.mapRevealEnabled
    SFramesDB.MapReveal.unexploredAlpha = c.mapRevealAlpha

    if type(SFramesDB.WorldMap) ~= "table" then SFramesDB.WorldMap = {} end
    SFramesDB.WorldMap.enabled = c.worldMapEnabled

    SFrames.Theme:Apply(c.useClassTheme and SFrames.Theme:GetCurrentPreset() or c.themePreset)
    SFramesDB.setupComplete = true
end

--------------------------------------------------------------------------------
-- Step builders (lazy — only called first time user navigates to that step)
--------------------------------------------------------------------------------

-- Step 1: Welcome -----------------------------------------------------------
local function BuildWelcome(page)
    local t = T()
    local font = SFrames:GetFont()

    local logoIco = SFrames:CreateIcon(page, "logo", 52)
    logoIco:SetDrawLayer("OVERLAY")
    logoIco:SetPoint("TOP", page, "TOP", 0, -44)

    local logo = page:CreateFontString(nil, "OVERLAY")
    logo:SetFont(font, 22, "OUTLINE")
    logo:SetPoint("TOP", logoIco, "BOTTOM", 0, -8)
    logo:SetText("Nanami-UI")
    logo:SetTextColor(t.accentLight[1], t.accentLight[2], t.accentLight[3])

    local sub = page:CreateFontString(nil, "OVERLAY")
    sub:SetFont(font, 11, "OUTLINE")
    sub:SetPoint("TOP", logo, "BOTTOM", 0, -4)
    sub:SetText("v1.0.0")
    sub:SetTextColor(t.dimText[1], t.dimText[2], t.dimText[3])

    local tagline = page:CreateFontString(nil, "OVERLAY")
    tagline:SetFont(font, 10, "OUTLINE")
    tagline:SetPoint("TOP", sub, "BOTTOM", 0, -14)
    tagline:SetWidth(440); tagline:SetJustifyH("CENTER")
    tagline:SetText("一站式现代 UI 重塑方案，为经典怀旧而生")
    tagline:SetTextColor(t.accentLight[1], t.accentLight[2], t.accentLight[3])

    local desc = page:CreateFontString(nil, "OVERLAY")
    desc:SetFont(font, 9.5, "OUTLINE")
    desc:SetPoint("TOP", tagline, "BOTTOM", 0, -12)
    desc:SetWidth(440); desc:SetJustifyH("CENTER")
    desc:SetText(
        "9 套可选主题配色 + 职业自适应配色\n"..
        "全面重设计：商人 / 任务 / 技能书 / 邮箱 / 社交 等 12+ 界面\n"..
        "现代化单位框体、动作条、聊天框与 Buff 栏\n"..
        "AI 翻译、飞行助手、地图增强、AFK 待机动画\n"..
        "轻量高效，所有功能均可自由开关\n\n"..
        "接下来将引导你完成基础配置，\n"..
        "你也可以随时跳过，直接使用默认设置。"
    )
    desc:SetTextColor(0.78, 0.72, 0.76)

    local startBtn = MakeButton(page, "开始配置", 160, 32, function()
        SW:GoTo(2)
    end)
    startBtn:SetPoint("TOP", desc, "BOTTOM", 0, -18)

    local skipBtn = MakeButton(page, "跳过，使用默认配置", 200, 28, function()
        SW:DoSkip()
    end)
    skipBtn:SetPoint("TOP", startBtn, "BOTTOM", 0, -10)

    page:SetHeight(CONTENT_H)
end

-- Step 2: Theme & Skins ----------------------------------------------------
local function BuildTheme(page)
    local t = T()
    local font = SFrames:GetFont()

    local sec1 = MakeSection(page, "主题配色", 0, 0, CONTENT_W, 100, "star")
    local presets = SFrames.Theme.PresetOrder
    local swatches = {}
    for idx = 1, table.getn(presets) do
        local key = presets[idx]
        local p = SFrames.Theme.Presets[key]
        local sw = CreateFrame("Button", WN("Sw"), sec1)
        sw:SetWidth(28); sw:SetHeight(28)
        sw:SetPoint("TOPLEFT", sec1, "TOPLEFT", 14 + (idx-1)*34, -34)
        sw:SetBackdrop({
            bgFile   = "Interface\\Buttons\\WHITE8X8",
            edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
            tile = false, edgeSize = 8,
            insets = { left = 2, right = 2, top = 2, bottom = 2 },
        })
        local hr, hg, hb = SFrames.Theme.HSVtoRGB(p.hue, 0.40 * (p.satMul or 1), 0.80)
        sw:SetBackdropColor(hr, hg, hb, 1)
        sw.presetKey = key
        sw:SetScript("OnClick", function()
            choices.themePreset = this.presetKey
            choices.useClassTheme = false
            SFrames.Theme:Apply(this.presetKey)
            SW:RefreshPanelColors()
            for _, s in ipairs(swatches) do
                if s.presetKey == this.presetKey then
                    s:SetBackdropBorderColor(1,1,1,1)
                else
                    s:SetBackdropBorderColor(0.25,0.25,0.25,1)
                end
            end
            if page._classCheck then page._classCheck:Refresh() end
            PlaySound("igMainMenuOptionCheckBoxOn")
        end)
        local active = choices.themePreset
        if key == active and not choices.useClassTheme then
            sw:SetBackdropBorderColor(1,1,1,1)
        else
            sw:SetBackdropBorderColor(0.25,0.25,0.25,1)
        end
        sw:SetScript("OnEnter", function()
            this:SetBackdropBorderColor(0.8,0.8,0.8,1)
            GameTooltip:SetOwner(this, "ANCHOR_TOP")
            GameTooltip:SetText(SFrames.Theme.Presets[this.presetKey].name)
            GameTooltip:Show()
        end)
        sw:SetScript("OnLeave", function()
            local act = choices.themePreset
            if this.presetKey == act and not choices.useClassTheme then
                this:SetBackdropBorderColor(1,1,1,1)
            else
                this:SetBackdropBorderColor(0.25,0.25,0.25,1)
            end
            GameTooltip:Hide()
        end)
        table.insert(swatches, sw)
    end
    page._classCheck = MakeCheck(sec1, "根据当前职业自动选择主题色", 14, -72,
        function() return choices.useClassTheme end,
        function(v) choices.useClassTheme = v
            if v then
                SFrames.Theme:Apply(SFrames.Theme:GetCurrentPreset())
            else
                SFrames.Theme:Apply(choices.themePreset)
            end
            SW:RefreshPanelColors()
        end)

    -- Icon set picker (right after theme color)
    local secIcon = MakeSection(page, "图标风格", 0, -108, CONTENT_W, 90, "star")
    MakeDesc(secIcon, "选择图标风格（8 套可选，重载 UI 后生效）", 14, -28, CONTENT_W - 30)

    local ISET_NAMES = { "icon", "icon2", "icon3", "icon4", "icon5", "icon6", "icon7", "icon8" }
    local ISET_SIZE = 36
    local ISET_GAP  = 8
    local wFaction = UnitFactionGroup and UnitFactionGroup("player") or "Alliance"
    local wFKey = (wFaction == "Horde") and "horde" or "alliance"
    local wFCoords = SFrames.ICON_TCOORDS and SFrames.ICON_TCOORDS[wFKey]
    if not wFCoords then
        wFCoords = (wFKey == "horde") and { 0.75, 0.875, 0, 0.125 } or { 0.625, 0.75, 0, 0.125 }
    end

    local isetBtns = {}
    for idx = 1, table.getn(ISET_NAMES) do
        local setKey = ISET_NAMES[idx]
        local texPath = "Interface\\AddOns\\Nanami-UI\\img\\" .. setKey
        local sx = 14 + (idx - 1) * (ISET_SIZE + ISET_GAP)

        local ib = CreateFrame("Button", WN("IS"), secIcon)
        ib:SetWidth(ISET_SIZE); ib:SetHeight(ISET_SIZE)
        ib:SetPoint("TOPLEFT", secIcon, "TOPLEFT", sx, -44)
        ib:SetBackdrop({
            bgFile   = "Interface\\Tooltips\\UI-Tooltip-Background",
            edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
            tile = true, tileSize = 16, edgeSize = 10,
            insets = { left = 2, right = 2, top = 2, bottom = 2 },
        })
        ib:SetBackdropColor(0.08, 0.08, 0.1, 0.85)
        ib:SetBackdropBorderColor(0.3, 0.3, 0.35, 1)

        local pv = ib:CreateTexture(nil, "ARTWORK")
        pv:SetTexture(texPath)
        pv:SetTexCoord(wFCoords[1], wFCoords[2], wFCoords[3], wFCoords[4])
        pv:SetPoint("CENTER", ib, "CENTER", 0, 0)
        pv:SetWidth(ISET_SIZE - 6); pv:SetHeight(ISET_SIZE - 6)

        ib.setKey = setKey
        ib:SetScript("OnClick", function()
            choices.iconSet = this.setKey
            for _, b in ipairs(isetBtns) do
                if b.setKey == this.setKey then
                    b:SetBackdropBorderColor(1, 0.85, 0.6, 1)
                else
                    b:SetBackdropBorderColor(0.3, 0.3, 0.35, 1)
                end
            end
            PlaySound("igMainMenuOptionCheckBoxOn")
        end)
        ib:SetScript("OnEnter", function()
            this:SetBackdropBorderColor(0.7, 0.7, 0.7, 1)
            GameTooltip:SetOwner(this, "ANCHOR_NONE")
            GameTooltip:ClearAllPoints()
            GameTooltip:SetPoint("BOTTOM", this, "TOP", 0, 6)
            local num = this.setKey == "icon" and "1" or string.sub(this.setKey, 5)
            GameTooltip:SetText("图标风格 " .. num .. (this.setKey == "icon" and " (默认)" or ""))
            GameTooltip:Show()
            GameTooltip:SetFrameStrata("TOOLTIP")
            GameTooltip:Raise()
        end)
        ib:SetScript("OnLeave", function()
            local cur = choices.iconSet or "icon"
            if this.setKey == cur then
                this:SetBackdropBorderColor(1, 0.85, 0.6, 1)
            else
                this:SetBackdropBorderColor(0.3, 0.3, 0.35, 1)
            end
            GameTooltip:Hide()
        end)

        local cur = choices.iconSet or "icon"
        if setKey == cur then
            ib:SetBackdropBorderColor(1, 0.85, 0.6, 1)
        end
        table.insert(isetBtns, ib)
    end

    -- Skin toggles (below icon picker)
    local sec2 = MakeSection(page, "界面皮肤替换", 0, -206, CONTENT_W, 150, "settings")
    MakeDesc(sec2, "选择启用哪些 Nanami-UI 美化界面（替换原生窗口）", 14, -28, CONTENT_W - 30)

    local skins = {
        { key = "enableMerchant",    label = "商人界面" },
        { key = "enableQuestUI",     label = "任务/NPC 对话" },
        { key = "enableQuestLogSkin",label = "任务日志" },
        { key = "enableSpellBook",   label = "技能书" },
        { key = "enableTradeSkill",  label = "专业技能" },
        { key = "enableTrainer",     label = "训练师" },
        { key = "charPanelEnable",   label = "人物面板" },
        { key = "enableMail",        label = "邮箱" },
        { key = "enablePetStable",   label = "兽栏" },
        { key = "enableSocial",      label = "社交" },
        { key = "enableInspect",     label = "观察面板" },
        { key = "enableFlightMap",   label = "飞行地图" },
    }
    local col, row = 0, 0
    for i = 1, table.getn(skins) do
        local sk = skins[i]
        local cx = 14 + col * 180
        local cy = -44 - row * 22
        MakeCheck(sec2, sk.label, cx, cy,
            function() return choices[sk.key] end,
            function(v) choices[sk.key] = v end)
        col = col + 1
        if col >= 3 then col = 0; row = row + 1 end
    end

    page:SetHeight(370)
end

-- Step 3: Chat & Translation ------------------------------------------------
local function BuildChat(page)
    local t = T()
    local sec1 = MakeSection(page, "聊天框", 0, 0, CONTENT_W, 80, "chat")

    local chatCb = MakeCheck(sec1, "启用 Nanami-UI 聊天框加强", 14, -34,
        function() return choices.enableChat end,
        function(v) choices.enableChat = v end)
    MakeDesc(sec1, "标签式多频道、频道过滤、样式美化、功能增强", 36, -52, CONTENT_W - 50)
    page._chatCb = chatCb

    local sec2 = MakeSection(page, "AI 翻译", 0, -90, CONTENT_W, 150, "ai")

    local transCb = MakeCheck(sec2, "启用聊天频道 AI 自动翻译", 14, -34,
        function() return choices.translateEnabled end,
        function(v)
            choices.translateEnabled = v
            if v then
                choices.enableChat = true
                if page._chatCb then page._chatCb:Refresh() end
            end
        end)

    MakeDesc(sec2,
        "需要 STranslate 外部程序和 API 支持。\n若不使用翻译功能，STranslate 无需运行。\n\n启用翻译将自动启用聊天框加强。\n翻译频道的详细配置请在聊天设置中调整。",
        14, -56, CONTENT_W - 30)

    page:SetHeight(250)
end

-- Step 4: Unit Frames + Action Bars (combined) -----------------------------
local function BuildFramesAndBars(page)
    -- Unit Frames section
    local sec1 = MakeSection(page, "单位框体", 0, 0, CONTENT_W, 90, "character")
    MakeCheck(sec1, "启用 Nanami-UI 单位框体", 14, -34,
        function() return choices.enableUnitFrames end,
        function(v) choices.enableUnitFrames = v end)
    MakeDesc(sec1,
        "替换原生玩家、目标、宠物、小队、团队框体为现代极简风格",
        14, -56, CONTENT_W - 30)

    -- Action Bars section
    local sec2 = MakeSection(page, "动作条", 0, -100, CONTENT_W, 210, "attack")
    local subFrame = CreateFrame("Frame", nil, sec2)
    subFrame:SetPoint("TOPLEFT", sec2, "TOPLEFT", 0, -60)
    subFrame:SetWidth(CONTENT_W); subFrame:SetHeight(140)

    MakeCheck(sec2, "启用 Nanami-UI 动作条接管", 14, -34,
        function() return choices.enableActionBars end,
        function(v)
            choices.enableActionBars = v
            if v then subFrame:Show() else subFrame:Hide() end
        end)
    MakeDesc(sec2, "需要 /reload 生效", 36, -52, 300)

    MakeCheck(subFrame, "显示宠物动作条", 30, -6,
        function() return choices.showPetBar end,
        function(v) choices.showPetBar = v end)
    MakeCheck(subFrame, "显示姿态栏 / 形态栏", 30, -30,
        function() return choices.showStanceBar end,
        function(v) choices.showStanceBar = v end)
    MakeCheck(subFrame, "显示右侧栏", 30, -54,
        function() return choices.showRightBars end,
        function(v) choices.showRightBars = v end)
    MakeCheck(subFrame, "按钮圆角", 30, -78,
        function() return choices.buttonRounded end,
        function(v) choices.buttonRounded = v end)
    MakeCheck(subFrame, "按钮内阴影", 230, -78,
        function() return choices.buttonInnerShadow end,
        function(v) choices.buttonInnerShadow = v end)

    if not choices.enableActionBars then subFrame:Hide() end
    page:SetHeight(320)
end

-- Step 6: Extras (with sub-options) -----------------------------------------
local function BuildExtras(page)
    local items = {}
    local allControls = {}

    local function AddFeature(label, desc, enableKey, subBuilder, iconKey)
        table.insert(items, { label = label, desc = desc, key = enableKey, subBuilder = subBuilder, icon = iconKey })
    end

    AddFeature("小地图美化", "自定义小地图外观", "minimapEnabled", function(p, x, y)
        MakeCheck(p, "显示时钟", x, y,
            function() return choices.minimapShowClock end,
            function(v) choices.minimapShowClock = v end)
        MakeCheck(p, "显示坐标", x + 160, y,
            function() return choices.minimapShowCoords end,
            function(v) choices.minimapShowCoords = v end)

        local function GetMode()
            local shape = choices.minimapMapShape or "circle"
            if shape == "square1" or shape == "square2" then return "square" end
            if (choices.minimapMapStyle or "auto") == "auto" then return "auto" end
            return "round"
        end

        local circleFrame = CreateFrame("Frame", nil, p)
        circleFrame:SetPoint("TOPLEFT", p, "TOPLEFT", x, y - 50)
        circleFrame:SetWidth(CONTENT_W)
        circleFrame:SetHeight(56)
        circleFrame:Hide()

        MakeLabel(circleFrame, "圆形边框:", 0, 0, 10, 0.78, 0.72, 0.76)
        local mapStyles = SFrames.Minimap and SFrames.Minimap.MAP_STYLES or {}
        local CS, CG = 28, 4
        local circleBtns = {}

        local autoStyleBtn = MakeButton(circleFrame, "自动", 38, CS, nil)
        autoStyleBtn:ClearAllPoints()
        autoStyleBtn:SetPoint("TOPLEFT", circleFrame, "TOPLEFT", 0, -16)

        local function RefreshCircleHighlight()
            local cur = choices.minimapMapStyle or "auto"
            local matched = (cur == "auto")
            for _, b in ipairs(circleBtns) do
                if b.styleKey == cur then
                    b:SetBackdropBorderColor(1, 0.78, 0.2, 1)
                    matched = true
                else
                    b:SetBackdropBorderColor(0.3, 0.3, 0.35, 1)
                end
            end
            if not matched then
                choices.minimapMapStyle = "auto"
            end
            autoStyleBtn.sfActive = ((choices.minimapMapStyle or "auto") == "auto")
            autoStyleBtn:RefreshVisual()
        end

        autoStyleBtn:SetScript("OnClick", function()
            choices.minimapMapStyle = "auto"
            RefreshCircleHighlight()
            PlaySound("igMainMenuOptionCheckBoxOn")
        end)

        for idx, style in ipairs(mapStyles) do
            local sb = CreateFrame("Button", WN("CS"), circleFrame)
            sb:SetWidth(CS); sb:SetHeight(CS)
            sb:SetPoint("TOPLEFT", circleFrame, "TOPLEFT",
                42 + (idx - 1) * (CS + CG), -16)
            sb:SetBackdrop({
                bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
                edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
                tile = true, tileSize = 16, edgeSize = 8,
                insets = { left = 2, right = 2, top = 2, bottom = 2 },
            })
            sb:SetBackdropColor(0.08, 0.08, 0.1, 0.85)

            local ptex = sb:CreateTexture(nil, "ARTWORK")
            ptex:SetTexture(style.tex)
            ptex:SetPoint("CENTER", sb, "CENTER", 0, 0)
            ptex:SetWidth(CS - 4); ptex:SetHeight(CS - 4)

            sb.styleKey = style.key
            sb._sfLabel = style.label
            sb:SetScript("OnClick", function()
                choices.minimapMapStyle = this.styleKey
                RefreshCircleHighlight()
                PlaySound("igMainMenuOptionCheckBoxOn")
            end)
            sb:SetScript("OnEnter", function()
                this:SetBackdropBorderColor(0.7, 0.7, 0.7, 1)
                GameTooltip:SetOwner(this, "ANCHOR_TOP")
                GameTooltip:SetText(this._sfLabel)
                GameTooltip:Show()
            end)
            sb:SetScript("OnLeave", function()
                local cur = choices.minimapMapStyle or "auto"
                if this.styleKey == cur then
                    this:SetBackdropBorderColor(1, 0.78, 0.2, 1)
                else
                    this:SetBackdropBorderColor(0.3, 0.3, 0.35, 1)
                end
                GameTooltip:Hide()
            end)

            table.insert(circleBtns, sb)
        end
        RefreshCircleHighlight()

        MakeLabel(p, "地图风格:", x, y - 26, 10, 0.78, 0.72, 0.76)
        MakeBtnGroup(p, x + 60, y - 24,
            { {key="auto", label="自动", w=50}, {key="round", label="圆形", w=50}, {key="square", label="方形", w=50} },
            GetMode,
            function(v)
                if v == "auto" then
                    choices.minimapMapShape = "circle"
                    choices.minimapMapStyle = "auto"
                    circleFrame:Hide()
                elseif v == "round" then
                    choices.minimapMapShape = "circle"
                    if choices.minimapMapStyle == "auto" then
                        choices.minimapMapStyle = "map"
                    end
                    RefreshCircleHighlight()
                    circleFrame:Show()
                elseif v == "square" then
                    choices.minimapMapShape = "square1"
                    circleFrame:Hide()
                end
            end)

        if GetMode() == "round" then circleFrame:Show() end
        return 110
    end, "worldmap")

    AddFeature("Buff 栏", "自定义 Buff/Debuff 显示", "buffEnabled", function(p, x, y)
        MakeSlider(p, "图标大小", x, y - 10, 180, 16, 50, 1,
            function() return choices.buffIconSize end,
            function(v) choices.buffIconSize = v end)
        MakeSlider(p, "每行数量", x + 220, y - 10, 180, 4, 16, 1,
            function() return choices.buffIconsPerRow end,
            function(v) choices.buffIconsPerRow = v end)
        MakeLabel(p, "增长方向:", x, y - 52, 10, 0.78, 0.72, 0.76)
        MakeBtnGroup(p, x + 60, y - 50,
            { {key="LEFT", label="向左", w=52}, {key="RIGHT", label="向右", w=52} },
            function() return choices.buffGrowDir end,
            function(v) choices.buffGrowDir = v end)
        MakeCheck(p, "显示计时器", x + 220, y - 50,
            function() return choices.buffShowTimer end,
            function(v) choices.buffShowTimer = v end)
        return 78
    end, "buff")

    AddFeature("AFK 待机动画", "进入暂离后显示全屏待机画面", "afkEnabled", function(p, x, y)
        MakeSlider(p, "触发延迟(分钟)", x, y - 10, 200, 1, 30, 1,
            function() return choices.afkDelay end,
            function(v) choices.afkDelay = v end)
        MakeCheck(p, "仅在休息区触发", x + 250, y - 4,
            function() return choices.afkOutsideRest end,
            function(v) choices.afkOutsideRest = v end)
        return 48
    end, "camp")

    AddFeature("地图迷雾揭示", "显示未探索区域的轮廓", "mapRevealEnabled", function(p, x, y)
        MakeSlider(p, "未探索区域透明度", x, y - 10, 240, 0, 1, 0.05,
            function() return choices.mapRevealAlpha end,
            function(v) choices.mapRevealAlpha = v end,
            function(v) return string.format("%.0f%%", v * 100) end)
        return 48
    end, "search")

    AddFeature("世界地图皮肤", "美化世界地图界面", "worldMapEnabled", nil, "worldmap")
    AddFeature("飞行助手", "美化飞行界面 + 飞行进度条", "enableFlightMap", nil, "mount")

    -- Build layout
    local yOff = -4
    for i = 1, table.getn(items) do
        local item = items[i]
        local sec = MakeSection(page, item.label, 0, yOff, CONTENT_W, 30, item.icon)
        MakeDesc(sec, item.desc, 14, -30, CONTENT_W - 30)
        sec:SetHeight(50)

        local subFrame = nil
        local subH = 0

        if item.subBuilder then
            subFrame = CreateFrame("Frame", nil, page)
            subFrame:SetWidth(CONTENT_W)
        end

        local cb = MakeCheck(sec, "启用", CONTENT_W - 80, -8,
            function() return choices[item.key] end,
            function(v)
                choices[item.key] = v
                if subFrame then
                    if v then subFrame:Show() else subFrame:Hide() end
                end
            end)

        if subFrame then
            subH = item.subBuilder(subFrame, 14, -4) or 40
            subFrame:SetHeight(subH)
            subFrame:SetPoint("TOPLEFT", page, "TOPLEFT", 0, yOff - 50)
            if not choices[item.key] then subFrame:Hide() end
            yOff = yOff - 50 - subH - 6
        else
            yOff = yOff - 56
        end
    end

    page:SetHeight(math.abs(yOff) + 10)
end

-- Step 7: Hardcore (conditional) --------------------------------------------
local function BuildHardcore(page)
    local sec = MakeSection(page, "硬核模式", 0, 0, CONTENT_W, 120, "skull")
    MakeDesc(sec, "检测到你的角色处于硬核模式，建议启用硬核频道接收\n以获取硬核死亡消息通知。", 14, -30, CONTENT_W - 30)

    MakeCheck(sec, "启用硬核频道接收", 14, -68,
        function() return not choices.hcGlobalDisable end,
        function(v) choices.hcGlobalDisable = not v end)
    MakeDesc(sec, "关闭将全局屏蔽硬核频道消息", 36, -86, 300)

    page:SetHeight(140)
end

-- Complete page --------------------------------------------------------------
local function BuildComplete(page)
    local t = T()
    local font = SFrames:GetFont()

    local doneIco = SFrames:CreateIcon(page, "logo", 48)
    doneIco:SetDrawLayer("OVERLAY")
    doneIco:SetPoint("TOP", page, "TOP", 0, -16)
    doneIco:SetVertexColor(t.accentLight[1], t.accentLight[2], t.accentLight[3])

    local icon = page:CreateFontString(nil, "OVERLAY")
    icon:SetFont(font, 28, "OUTLINE")
    icon:SetPoint("TOP", doneIco, "BOTTOM", 0, -4)
    icon:SetText("=^_^=")
    icon:SetTextColor(t.accentLight[1], t.accentLight[2], t.accentLight[3])

    local title = page:CreateFontString(nil, "OVERLAY")
    title:SetFont(font, 16, "OUTLINE")
    title:SetPoint("TOP", icon, "BOTTOM", 0, -10)
    title:SetText("配置完成！")
    title:SetTextColor(t.title[1], t.title[2], t.title[3])

    local desc = page:CreateFontString(nil, "OVERLAY")
    desc:SetFont(font, 10, "OUTLINE")
    desc:SetPoint("TOP", title, "BOTTOM", 0, -14)
    desc:SetWidth(420); desc:SetJustifyH("CENTER")
    desc:SetTextColor(0.78, 0.72, 0.76)

    if runMode == "rerun" then
        desc:SetText("设置已更新。部分选项需要 /reload 才能完全生效。\n你可以随时通过 /nui config 或 ESC 菜单调整设置。")
    else
        desc:SetText("所有设置已保存。\n你可以随时通过 /nui config 或 ESC 菜单调整设置。")
    end

    local doneBtn = MakeButton(page, "完成", 140, 32, function()
        SW:DoComplete()
    end)
    doneBtn:SetPoint("TOP", desc, "BOTTOM", 0, -24)

    page:SetHeight(CONTENT_H)
end

--------------------------------------------------------------------------------
-- Build main frame (once)
--------------------------------------------------------------------------------

local function EnsureWizardFrame()
    if built then return end
    built = true

    local t = T()

    -- Full-screen overlay
    overlay = CreateFrame("Frame", "NanamiWizOverlay", UIParent)
    overlay:SetFrameStrata("DIALOG")
    overlay:SetAllPoints(UIParent)
    overlay:EnableMouse(true)
    overlay:SetScript("OnMouseDown", function() end)
    local bg = overlay:CreateTexture(nil, "BACKGROUND")
    bg:SetAllPoints(overlay)
    bg:SetTexture(0, 0, 0, 0.6)

    -- Center panel
    panel = CreateFrame("Frame", "NanamiWizPanel", overlay)
    panel:SetWidth(PANEL_W); panel:SetHeight(PANEL_H)
    panel:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
    panel:SetBackdrop({
        bgFile   = "Interface\\Tooltips\\UI-Tooltip-Background",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true, tileSize = 16, edgeSize = 14,
        insets = { left = 3, right = 3, top = 3, bottom = 3 },
    })
    panel:SetBackdropColor(t.panelBg[1], t.panelBg[2], t.panelBg[3], t.panelBg[4])
    panel:SetBackdropBorderColor(t.panelBorder[1], t.panelBorder[2], t.panelBorder[3], t.panelBorder[4])

    -- Header
    headerTitle = panel:CreateFontString(nil, "OVERLAY")
    headerTitle:SetFont(SFrames:GetFont(), 13, "OUTLINE")
    headerTitle:SetPoint("TOP", panel, "TOP", 0, -14)
    headerTitle:SetTextColor(t.title[1], t.title[2], t.title[3])

    -- Step dots row
    local dotHolder = CreateFrame("Frame", nil, panel)
    dotHolder:SetWidth(PANEL_W); dotHolder:SetHeight(16)
    dotHolder:SetPoint("TOP", panel, "TOP", 0, -34)

    -- Content scroll area
    local contentHolder = CreateFrame("Frame", nil, panel)
    contentHolder:SetWidth(CONTENT_W); contentHolder:SetHeight(CONTENT_H)
    contentHolder:SetPoint("TOPLEFT", panel, "TOPLEFT", 20, -56)

    contentScroll = CreateFrame("ScrollFrame", WN("Scr"), contentHolder)
    contentScroll:SetAllPoints(contentHolder)

    contentChild = CreateFrame("Frame", nil, contentScroll)
    contentChild:SetWidth(CONTENT_W)
    contentChild:SetHeight(CONTENT_H)
    contentScroll:SetScrollChild(contentChild)

    contentHolder:EnableMouseWheel(true)
    contentHolder:SetScript("OnMouseWheel", function()
        local cur = contentScroll:GetVerticalScroll()
        local maxS = math.max(0, contentChild:GetHeight() - CONTENT_H)
        local nv = cur - arg1 * 30
        if nv < 0 then nv = 0 end
        if nv > maxS then nv = maxS end
        contentScroll:SetVerticalScroll(nv)
    end)

    -- Footer navigation
    local footer = CreateFrame("Frame", nil, panel)
    footer:SetWidth(PANEL_W - 40); footer:SetHeight(36)
    footer:SetPoint("BOTTOM", panel, "BOTTOM", 0, 12)

    btnPrev = MakeButton(footer, "< 上一步", 120, 28, function() SW:GoPrev() end)
    btnPrev:SetPoint("LEFT", footer, "LEFT", 0, 0)

    stepLabel = footer:CreateFontString(nil, "OVERLAY")
    stepLabel:SetFont(SFrames:GetFont(), 10, "OUTLINE")
    stepLabel:SetPoint("CENTER", footer, "CENTER", 0, 0)
    stepLabel:SetTextColor(t.dimText[1], t.dimText[2], t.dimText[3])

    btnNext = MakeButton(footer, "下一步 >", 120, 28, function() SW:GoNext() end)
    btnNext:SetPoint("RIGHT", footer, "RIGHT", 0, 0)

    btnSkip = CreateFrame("Button", WN("Skip"), footer)
    btnSkip:SetWidth(60); btnSkip:SetHeight(16)
    btnSkip:SetPoint("BOTTOMRIGHT", panel, "BOTTOMRIGHT", -14, 4)
    local skipFs = btnSkip:CreateFontString(nil, "OVERLAY")
    skipFs:SetFont(SFrames:GetFont(), 9, "OUTLINE")
    skipFs:SetAllPoints(btnSkip)
    skipFs:SetText("跳过全部")
    skipFs:SetTextColor(0.55, 0.50, 0.54)
    btnSkip:SetScript("OnClick", function() SW:DoSkip() end)
    btnSkip:SetScript("OnEnter", function() skipFs:SetTextColor(0.85, 0.75, 0.80) end)
    btnSkip:SetScript("OnLeave", function() skipFs:SetTextColor(0.55, 0.50, 0.54) end)

    -- Store dot holder for later
    panel._dotHolder = dotHolder
end

--------------------------------------------------------------------------------
-- Step list & dot creation
--------------------------------------------------------------------------------

local function BuildStepList()
    stepList = {}
    table.insert(stepList, { id = "welcome",   title = "欢迎",       builder = BuildWelcome })
    table.insert(stepList, { id = "theme",      title = "主题与皮肤", builder = BuildTheme })
    table.insert(stepList, { id = "chat",       title = "聊天与翻译",     builder = BuildChat })
    table.insert(stepList, { id = "framesbars", title = "框体与动作条",   builder = BuildFramesAndBars })
    table.insert(stepList, { id = "extras",     title = "辅助功能",       builder = BuildExtras })

    -- Hardcore: only if player is hardcore
    local isHC = false
    if C_TurtleWoW and C_TurtleWoW.IsHardcore then
        local ok, val = pcall(C_TurtleWoW.IsHardcore, "player")
        if ok and val then isHC = true end
    end
    if isHC then
        table.insert(stepList, { id = "hardcore", title = "硬核模式", builder = BuildHardcore })
    end

    table.insert(stepList, { id = "complete", title = "完成", builder = BuildComplete })

    -- Create step pages (empty frames, lazily populated)
    for _, pg in ipairs(stepPages) do if pg then pg:Hide() end end
    stepPages = {}
    for i = 1, table.getn(stepList) do
        local pg = CreateFrame("Frame", nil, contentChild)
        pg:SetWidth(CONTENT_W)
        pg:SetHeight(CONTENT_H)
        pg:SetPoint("TOPLEFT", contentChild, "TOPLEFT", 0, 0)
        pg:Hide()
        pg._built = false
        stepPages[i] = pg
    end

    -- Create dots
    for _, d in ipairs(stepDots) do if d then d:Hide() end end
    stepDots = {}
    local totalDots = table.getn(stepList)
    local dotSpacing = 18
    local startX = (PANEL_W - totalDots * dotSpacing) / 2
    for i = 1, totalDots do
        local dot = CreateFrame("Frame", nil, panel._dotHolder)
        dot:SetWidth(8); dot:SetHeight(8)
        dot:SetPoint("TOPLEFT", panel._dotHolder, "TOPLEFT", startX + (i-1)*dotSpacing, -4)
        dot:SetBackdrop({
            bgFile   = "Interface\\Buttons\\WHITE8X8",
            edgeFile = "Interface\\Buttons\\WHITE8X8",
            tile = false, edgeSize = 1,
            insets = { left = 0, right = 0, top = 0, bottom = 0 },
        })
        stepDots[i] = dot
    end
end

--------------------------------------------------------------------------------
-- Navigation
--------------------------------------------------------------------------------

function SW:RefreshPanelColors()
    if not panel then return end
    local t = T()
    panel:SetBackdropColor(t.panelBg[1], t.panelBg[2], t.panelBg[3], t.panelBg[4])
    panel:SetBackdropBorderColor(t.panelBorder[1], t.panelBorder[2], t.panelBorder[3], t.panelBorder[4])
    if headerTitle then headerTitle:SetTextColor(t.title[1], t.title[2], t.title[3]) end
end

function SW:GoTo(idx)
    if idx < 1 or idx > table.getn(stepList) then return end
    currentStep = idx

    -- Lazily build
    local pg = stepPages[idx]
    if not pg._built then
        pg._built = true
        local ok, err = pcall(stepList[idx].builder, pg)
        if not ok then
            DEFAULT_CHAT_FRAME:AddMessage("|cffff4444[Nanami-UI] Wizard step error ("..stepList[idx].id.."): "..tostring(err).."|r")
        end
    end

    -- Show/hide pages
    for i = 1, table.getn(stepPages) do
        if i == idx then stepPages[i]:Show() else stepPages[i]:Hide() end
    end

    -- Set scroll child height and reset scroll
    contentChild:SetHeight(math.max(pg:GetHeight(), CONTENT_H))
    contentScroll:SetVerticalScroll(0)

    -- Update header
    headerTitle:SetText(stepList[idx].title)

    -- Update dots
    local t = T()
    for i = 1, table.getn(stepDots) do
        local d = stepDots[i]
        if i == idx then
            d:SetBackdropColor(t.accentLight[1], t.accentLight[2], t.accentLight[3], 1)
            d:SetBackdropBorderColor(t.accentLight[1], t.accentLight[2], t.accentLight[3], 1)
        elseif i < idx then
            d:SetBackdropColor(t.accent[1], t.accent[2], t.accent[3], 0.5)
            d:SetBackdropBorderColor(t.accent[1], t.accent[2], t.accent[3], 0.5)
        else
            d:SetBackdropColor(0.3, 0.3, 0.3, 0.6)
            d:SetBackdropBorderColor(0.3, 0.3, 0.3, 0.6)
        end
    end

    -- Update step label
    stepLabel:SetText(idx .. " / " .. table.getn(stepList))

    -- Navigation buttons
    local isFirst   = (idx == 1)
    local isLast    = (idx == table.getn(stepList))
    local isWelcome = (stepList[idx].id == "welcome")
    local isDone    = (stepList[idx].id == "complete")

    if isWelcome then
        btnPrev:Hide(); btnNext:Hide(); btnSkip:Hide(); stepLabel:SetText("")
    elseif isDone then
        btnPrev:Show(); btnNext:Hide(); btnSkip:Hide()
    else
        btnPrev:Show(); btnNext:Show(); btnSkip:Show()
        if isFirst then btnPrev:Hide() end
    end
end

function SW:GoNext()
    if currentStep < table.getn(stepList) then
        self:GoTo(currentStep + 1)
    end
end

function SW:GoPrev()
    if currentStep > 1 then
        self:GoTo(currentStep - 1)
    end
end

function SW:DoSkip()
    if runMode == "rerun" then
        self:Hide()
        return
    end
    -- First-run: apply defaults
    if not SFramesDB then SFramesDB = {} end
    SFramesDB.setupComplete = true
    self:Hide()
    if completeCb then completeCb() end
end

function SW:DoComplete()
    local ok, err = pcall(ApplyChoices)
    if not ok then
        DEFAULT_CHAT_FRAME:AddMessage("|cffff4444[Nanami-UI] Wizard apply error: "..tostring(err).."|r")
        if not SFramesDB then SFramesDB = {} end
        SFramesDB.setupComplete = true
    end
    self:Hide()
    ReloadUI()
end

--------------------------------------------------------------------------------
-- Public API
--------------------------------------------------------------------------------

function SW:Show(callback, mode)
    local ok, err = pcall(function()
        runMode = mode or "firstrun"
        completeCb = callback

        if runMode == "rerun" then
            choices = GetCurrentChoices()
        else
            choices = GetDefaultChoices()
        end

        EnsureWizardFrame()
        BuildStepList()

        overlay:Show()
        overlay:Raise()

        self:GoTo(1)
    end)
    if not ok then
        DEFAULT_CHAT_FRAME:AddMessage("|cffff4444[Nanami-UI] Setup wizard failed: "..tostring(err).."|r")
        if not SFramesDB then SFramesDB = {} end
        SFramesDB.setupComplete = true
        if callback then callback() end
    end
end

function SW:Hide()
    if overlay then overlay:Hide() end
end
