local addonName, FN = ...
FN = FN or FavoriteNeighborhood or {}
FavoriteNeighborhood = FN

local L = FN.L or {}

FN.UI = FN.UI or {}
FN.UI.Quick = FN.UI.Quick or {}

-- Account-based options UI only.
-- Legacy character/mode/custom UI helpers removed on purpose.
-- Registered characters are shown only in Blizzard AddOn Options via Modules/RegisteredCharacters.lua.

local PAD_L = 20
local PAD_R = 20
local HEADER_X = PAD_L + 12
local LABEL_X  = PAD_L + 12
local CONTROL_VIS_X = 210
local DD_FRAME_X = CONTROL_VIS_X - 16

local QUICK_W = 440
local GAP_AFTER_TITLE   = 5
local GAP_AFTER_TEXT    = 14
local GAP_AFTER_HEADER  = 14
local GAP_BETWEEN_ROWS  = 10
local GAP_BETWEEN_GROUP = 16
local H_DROPDOWN = 34

local function SetPointPX(f, point, rel, relPoint, x, y)
    if PixelUtil and PixelUtil.SetPoint then
        local ok = pcall(PixelUtil.SetPoint, f, point, rel, relPoint, x, y)
        if ok then return end
    end
    f:SetPoint(point, rel, relPoint, x, y)
end

local function SetHeightPX(f, h)
    if PixelUtil and PixelUtil.SetHeight then
        local ok = pcall(PixelUtil.SetHeight, f, h)
        if ok then return end
    end
    f:SetHeight(h)
end

local function CalcDropDownWidth(parent, strings)
    local m = parent:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
    m:Hide()
    local maxW = 0
    for _, s in ipairs(strings or {}) do
        m:SetText(s or "")
        local w = m:GetStringWidth() or 0
        if w > maxW then maxW = w end
    end
    local wanted = math.floor(maxW + 48 + 0.5)
    if wanted < 120 then wanted = 120 end
    if wanted > 170 then wanted = 170 end
    return wanted
end


local function CreateProgressBar(parent, width, height)
    local holder = CreateFrame("Frame", nil, parent, "BackdropTemplate")
    holder:SetSize(width, height)
    holder:EnableMouse(true)
    holder:SetClipsChildren(false)

    -- Keep the frame itself invisible so the rounded housing atlas defines the shape.
    holder:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\Buttons\\WHITE8X8",
        edgeSize = 1,
    })
    holder:SetBackdropColor(0, 0, 0, 0)
    holder:SetBackdropBorderColor(0, 0, 0, 0)

    local inset = 0
    local FILL_ATLAS = "housing-dashboard-fillbar-fill"
    local BG_ATLAS   = "housing-dashboard-fillbar-bar-bg"
    local FALLBACK   = "Interface\\Buttons\\WHITE8X8"

    holder._usingAtlas = false

    local bg = holder:CreateTexture(nil, "BACKGROUND")
    bg:SetAllPoints(holder)
    do
        local ok = false
        if bg.SetAtlas then
            ok = pcall(bg.SetAtlas, bg, BG_ATLAS)
        end
        if ok then
            bg:SetVertexColor(1, 1, 1, 1)
            holder._usingAtlas = true
        else
            bg:SetTexture(FALLBACK)
            bg:SetColorTexture(0.05, 0.07, 0.12, 0.92)
        end
    end
    holder.bg = bg

    local fillClip = CreateFrame("Frame", nil, holder)
    fillClip:SetPoint("TOPLEFT", holder, "TOPLEFT", 2, -2)
    fillClip:SetPoint("BOTTOMLEFT", holder, "BOTTOMLEFT", 2, 2)
    fillClip:SetWidth(1)
    fillClip:SetClipsChildren(true)
    holder.fillClip = fillClip

    local fill = fillClip:CreateTexture(nil, "ARTWORK")
    fill:SetPoint("TOPLEFT", fillClip, "TOPLEFT", 0, 0)
    fill:SetPoint("BOTTOMLEFT", fillClip, "BOTTOMLEFT", 0, 0)
    fill:SetWidth(math.max(1, width - 4))
    do
        local ok = false
        if fill.SetAtlas then
            ok = pcall(fill.SetAtlas, fill, FILL_ATLAS)
        end
        if ok then
            if fill.SetDesaturated then
                fill:SetDesaturated(true)
            end
            fill:SetVertexColor(0.24, 0.56, 1.00, 1.00)
            holder._usingAtlas = true
        else
            fill:SetTexture(FALLBACK)
            fill:SetVertexColor(0.22, 0.55, 1.00, 1.00)
        end
    end
    holder.fill = fill

    local shine = fillClip:CreateTexture(nil, "OVERLAY")
    shine:SetAllPoints(fill)
    do
        local ok = false
        if shine.SetAtlas then
            ok = pcall(shine.SetAtlas, shine, FILL_ATLAS)
        end
        if ok then
            if shine.SetDesaturated then
                shine:SetDesaturated(true)
            end
            shine:SetVertexColor(1, 1, 1, 0.10)
            shine:SetBlendMode("ADD")
            shine:Show()
        else
            shine:Hide()
        end
    end
    holder.shine = shine

    local fullGlow = fillClip:CreateTexture(nil, "OVERLAY")
    fullGlow:SetAllPoints(fill)
    do
        local ok = false
        if fullGlow.SetAtlas then
            ok = pcall(fullGlow.SetAtlas, fullGlow, FILL_ATLAS)
        end
        if ok then
            if fullGlow.SetDesaturated then
                fullGlow:SetDesaturated(true)
            end
            fullGlow:SetVertexColor(0.35, 0.65, 1, 0.90)
            fullGlow:SetBlendMode("ADD")
            fullGlow:SetAlpha(0)
            fullGlow:Show()
        else
            fullGlow:SetTexture(FALLBACK)
            fullGlow:SetVertexColor(0.30, 0.60, 1.00, 0.16)
            fullGlow:SetBlendMode("ADD")
            fullGlow:SetAlpha(0)
        end
    end
    holder.fullGlow = fullGlow

	-- Full bar glow pulse animation
local fullGlowAnim = fullGlow:CreateAnimationGroup()
fullGlowAnim:SetLooping("REPEAT")

local glowFadeIn = fullGlowAnim:CreateAnimation("Alpha")
glowFadeIn:SetOrder(1)
glowFadeIn:SetFromAlpha(0.14)
glowFadeIn:SetToAlpha(0.75)
glowFadeIn:SetDuration(0.8)
glowFadeIn:SetSmoothing("IN_OUT")

local glowFadeOut = fullGlowAnim:CreateAnimation("Alpha")
glowFadeOut:SetOrder(2)
glowFadeOut:SetFromAlpha(0.75)
glowFadeOut:SetToAlpha(0.14)
glowFadeOut:SetDuration(0.8)
glowFadeOut:SetSmoothing("IN_OUT")

holder.fullGlowAnim = fullGlowAnim

    local textLayer = CreateFrame("Frame", nil, holder)
    textLayer:SetAllPoints(holder)
    textLayer:SetFrameStrata(holder:GetFrameStrata())
    textLayer:SetFrameLevel((fillClip:GetFrameLevel() or holder:GetFrameLevel()) + 10)
    holder.textLayer = textLayer

    local textShadow = textLayer:CreateFontString(nil, "ARTWORK", "GameFontNormal")
    textShadow:SetDrawLayer("ARTWORK", 6)
    textShadow:SetPoint("RIGHT", holder, "RIGHT", -9, -1)
    textShadow:SetJustifyH("RIGHT")
    textShadow:SetText("0%")
    textShadow:SetTextColor(0, 0, 0, 0.85)
    holder.textShadow = textShadow

    local text = textLayer:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    text:SetDrawLayer("OVERLAY", 7)
    text:SetPoint("RIGHT", holder, "RIGHT", -10, 0)
    text:SetJustifyH("RIGHT")
    text:SetText("0%")
    text:SetTextColor(1, 0.88, 0.20)
    holder.text = text

    local ornamentLayer = CreateFrame("Frame", nil, holder)
    ornamentLayer:SetAllPoints(holder)
    ornamentLayer:SetFrameStrata(holder:GetFrameStrata())
    ornamentLayer:SetFrameLevel((fillClip:GetFrameLevel() or holder:GetFrameLevel()) + 6)
    holder.ornamentLayer = ornamentLayer

    local PIP_ATLAS_PENDING = "housing-dashboard-fillbar-pip"
    local PIP_ATLAS_COMPLETE = "housing-dashboard-fillbar-pip-complete"
    local PIP_ATLAS_GLOW = "housing-dashboard-fillbar-pip-glow"

    local function CreateDiamond(parent, size, xOffset, yOffset)
        local glow = parent:CreateTexture(nil, "OVERLAY")
        glow:SetDrawLayer("OVERLAY", 1)
        glow:SetSize(size + 10, size + 10)
        glow:SetPoint("CENTER", holder, "LEFT", xOffset, yOffset or 0)
        if glow.SetAtlas then
            glow:SetAtlas(PIP_ATLAS_GLOW)
            glow:SetVertexColor(1, 1, 1, 1)
        else
            glow:SetTexture(FALLBACK)
            glow:SetRotation(math.rad(45))
            glow:SetVertexColor(0.95, 0.78, 0.20, 0.35)
        end
        glow:SetAlpha(0)

        local shadow = parent:CreateTexture(nil, "OVERLAY")
        shadow:SetDrawLayer("OVERLAY", 2)
        shadow:SetSize(size + 1, size + 1)
        shadow:SetPoint("CENTER", holder, "LEFT", xOffset + 1, (yOffset or 0) - 1)
        if shadow.SetAtlas then
            shadow:SetAtlas(PIP_ATLAS_PENDING)
        else
            shadow:SetTexture(FALLBACK)
            shadow:SetRotation(math.rad(45))
        end
        shadow:SetVertexColor(0, 0, 0, 0.35)

        local diamond = parent:CreateTexture(nil, "OVERLAY")
        diamond:SetDrawLayer("OVERLAY", 3)
        diamond:SetSize(size, size)
        diamond:SetPoint("CENTER", holder, "LEFT", xOffset, yOffset or 0)
        if diamond.SetAtlas then
            diamond:SetAtlas(PIP_ATLAS_PENDING)
        else
            diamond:SetTexture(FALLBACK)
            diamond:SetRotation(math.rad(45))
            diamond:SetVertexColor(0.85, 0.68, 0.12, 0.92)
        end

        return { glow = glow, shadow = shadow, diamond = diamond }
    end

    local markerY = -(math.floor(height * 0.18 + 0.5)) - 4
    local usableMarkerWidth = math.max(1, width - 4)
    local marker25X = math.floor((2 + usableMarkerWidth * 0.25) + 0.5)
    local marker50X = math.floor((2 + usableMarkerWidth * 0.50) + 0.5)
    local marker75X = math.floor((2 + usableMarkerWidth * 0.75) + 0.5)

    local function CreateTick(parent, xOffset)
        local tick = parent:CreateTexture(nil, "ARTWORK")
        tick:SetDrawLayer("ARTWORK", 0)
        tick:SetTexture(FALLBACK)
        tick:SetSize(2, math.max(8, height - 4))
        tick:SetPoint("CENTER", holder, "LEFT", xOffset, 0)
        tick:SetVertexColor(0.95, 0.78, 0.22, 0.95)
        return tick
    end


    holder.ticks = {
        CreateTick(ornamentLayer, marker25X),
        CreateTick(ornamentLayer, marker50X),
        CreateTick(ornamentLayer, marker75X),
    }

    holder.markers = {
        CreateDiamond(ornamentLayer, 15, marker25X, markerY),
        CreateDiamond(ornamentLayer, 15, marker50X, markerY),
        CreateDiamond(ornamentLayer, 15, marker75X, markerY),
    }

    holder.warning = holder:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    holder.warning:SetDrawLayer("OVERLAY", 1)
    holder.warning:SetPoint("TOP", holder, "BOTTOM", 0, -25)
    holder.warning:SetJustifyH("CENTER")
    holder.warning:SetText("")
    holder.warning:Hide()

    holder.data = nil

    holder:SetScript("OnEnter", function(self)
        if not self.data then return end
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        local n = self.data.name or L["None"]
        GameTooltip:AddLine(L["Favorite Neighborhood"], 1, 0.82, 0)
        GameTooltip:AddLine(n, 1, 1, 1)
        local p = tonumber(self.data.percent) or 0
        GameTooltip:AddLine(string.format(L["Progress: %d%%"], p), 0.8, 0.9, 1)
        if self.data.currentProgress and self.data.maxProgress and self.data.maxProgress > 0 then
            local cp = tonumber(self.data.currentProgress) or 0
            local mp = tonumber(self.data.maxProgress) or 0
            GameTooltip:AddLine(string.format("%d / %d", math.floor(cp + 0.5), math.floor(mp + 0.5)), 0.7, 0.7, 0.7)
        end
        local showRecommended = FN.AutoSwitch and FN.AutoSwitch.ShouldShowSwitchRecommended and FN.AutoSwitch:ShouldShowSwitchRecommended(self.data.id)
        if showRecommended then
            GameTooltip:AddLine(L["Completed - switching recommended"], 1, 0.5, 0.5)
        end
        if self._usingAtlas then
            GameTooltip:AddLine(L["Using housing progress bar texture"], 0.55, 0.75, 1)
        end
        GameTooltip:Show()
    end)
    holder:SetScript("OnLeave", function() GameTooltip:Hide() end)

    function holder:SetProgressInfo(data)
        self.data = data
        local percent = (data and tonumber(data.percent)) or 0
        if percent < 0 then percent = 0 end
        if percent > 100 then percent = 100 end

        local usableWidth = math.max(1, self:GetWidth() - 4)
        local fillWidth = math.max(1, math.floor(usableWidth * (percent / 100) + 0.5))
        if self.fillClip then
            self.fillClip:SetWidth(fillWidth)
        end
        if self.fill then
            self.fill:SetWidth(usableWidth)
        end

        local label = string.format("%d%%", percent)
        self.text:SetText(label)
        self.textShadow:SetText(label)
        self.text:Show()
        self.textShadow:Show()

        local function ApplyDiamondVisual(diamondSet, reached)
    if not diamondSet or not diamondSet.diamond then return end

    if diamondSet.diamond.SetAtlas then
        diamondSet.diamond:SetAtlas(PIP_ATLAS_COMPLETE)
    end

    -- feste Diamantfarbe
    diamondSet.diamond:SetVertexColor(1, 0.88, 0.25, 1)

    if diamondSet.shadow then
        diamondSet.shadow:SetAlpha(0.35)
    end

    -- Glow nur wenn erreicht
    if diamondSet.glow then
        diamondSet.glow:SetAlpha(reached and 0.95 or 0)
    end
end

        if self.markers then
            local thresholds = {25, 50, 75}
            for i, marker in ipairs(self.markers) do
                ApplyDiamondVisual(marker, percent >= thresholds[i])
            end
        end

        local isFull = percent >= 100
        if self.fullGlow then
            if isFull then
                self.fullGlow:Show()
                if self.fullGlowAnim and not self.fullGlowAnim:IsPlaying() then
                    self.fullGlowAnim:Play()
                end
            elseif data and data.debugGlowAlpha ~= nil then
                if self.fullGlowAnim and self.fullGlowAnim:IsPlaying() then
                    self.fullGlowAnim:Stop()
                end
                self.fullGlow:SetAlpha(data.debugGlowAlpha)
            else
                if self.fullGlowAnim and self.fullGlowAnim:IsPlaying() then
                    self.fullGlowAnim:Stop()
                end
                self.fullGlow:SetAlpha(0)
            end
        end

        if data and data.debugWarningText then
            local c = data.debugWarningColor or {1, 1, 1}
            self.warning:SetText(data.debugWarningText)
            self.warning:SetTextColor(c[1] or 1, c[2] or 1, c[3] or 1)
            self.warning:Show()
            return
        end

        if data and data.warningText and data.warningText ~= "" then
            local c = data.warningColor or {1, 1, 1}
            self.warning:SetText(data.warningText)
            self.warning:SetTextColor(c[1] or 1, c[2] or 1, c[3] or 1)
            self.warning:Show()
        else
            self.warning:Hide()
        end
    end

    return holder
end

local function CreateBlueButton(parent, text, width, height)
    local b = CreateFrame("Button", nil, parent, "BackdropTemplate")
    b:SetSize(width or 140, height or 22)
    b:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        edgeSize = 14,
        insets = { left = 3, right = 3, top = 3, bottom = 3 },
    })
    b:SetBackdropColor(0.10, 0.40, 0.80, 0.95)
    b:SetBackdropBorderColor(0.00, 0.44, 0.87, 1)

    local fs = b:CreateFontString(nil, "ARTWORK", "GameFontNormal")
    fs:SetPoint("CENTER")
    fs:SetText(text or "")
    b._text = fs

    b:SetScript("OnEnter", function(self) self:SetBackdropColor(0.14, 0.50, 0.95, 1) end)
    b:SetScript("OnLeave", function(self) self:SetBackdropColor(0.10, 0.40, 0.80, 0.95) end)
    return b
end

local function BuildNeighborhoodOptions()
    if FN.Debug and FN.Debug.IsEnabled and FN.Debug:IsEnabled() and FN.Debug.GetDropdownNeighborhoods then
        local options = {}
        for _, item in ipairs(FN.Debug:GetDropdownNeighborhoods() or {}) do
            options[#options + 1] = { text = item.text or FN.ResolveNeighborhoodName(item.value), value = tostring(item.value) }
        end
        return options
    end

    local options, seen = {}, {}
    local function add(id)
        if not id then return end
        local sid = tostring(id)
        if seen[sid] then return end
        seen[sid] = true
        table.insert(options, { text = FN.ResolveNeighborhoodName(sid), value = sid })
    end

    add(FN_DB.global)
    add(FN.GetActiveNeighborhood())
    for id in pairs(FN_DB.neighborhoods or {}) do
        add(id)
    end

    table.sort(options, function(a,b) return tostring(a.text) < tostring(b.text) end)
    return options
end






local function SaveFramePosition(self)
    local point, _, _, x, y = self:GetPoint(1)
    FN_DB.ui.point = point or "CENTER"
    FN_DB.ui.x = x or 0
    FN_DB.ui.y = y or 0
end

function FN.UI.CreateQuickOptions()
    if FN.UI.Quick.frame then
        FN._rebuildingOptionsFrame = true
        FN.UI.Quick.frame:Hide()
        FN._rebuildingOptionsFrame = nil
        FN.UI.Quick.frame:SetParent(nil)
        FN.UI.Quick.frame = nil
        FN.UI.Quick.Refresh = nil
    end

    local quick = CreateFrame("Frame", "FavoriteNeighborhoodOptionsFrame", UIParent, "BackdropTemplate")
    quick:SetWidth(QUICK_W)
    quick:SetFrameStrata("DIALOG")
    quick:SetClampedToScreen(true)
    quick:Hide()
    quick:SetBackdrop({
        bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        edgeSize = 16,
        insets = { left = 4, right = 4, top = 4, bottom = 4 },
    })
    quick:SetBackdropColor(0, 0, 0, 0.85)
    quick:SetBackdropBorderColor(0, 0.44, 0.87, 1)
    quick:SetPoint(FN_DB.ui.point or "CENTER", UIParent, FN_DB.ui.point or "CENTER", FN_DB.ui.x or 0, FN_DB.ui.y or 0)

    quick:SetMovable(true)
    quick:EnableMouse(true)
    quick:EnableKeyboard(true)
    quick:SetPropagateKeyboardInput(true)
    quick:RegisterForDrag("LeftButton")
    quick:SetScript("OnDragStart", function(self) self:StartMoving() end)
    quick:SetScript("OnDragStop", function(self)
        self:StopMovingOrSizing()
        SaveFramePosition(self)
    end)
    quick:SetScript("OnKeyDown", function(self, key)
        if key == "ESCAPE" then
            self:SetPropagateKeyboardInput(false)
            self:Hide()
            return
        end
        self:SetPropagateKeyboardInput(true)
    end)

    local foundSpecial = false
    for _, name in ipairs(UISpecialFrames) do
        if name == "FavoriteNeighborhoodOptionsFrame" then
            foundSpecial = true
            break
        end
    end
    if not foundSpecial then
        table.insert(UISpecialFrames, "FavoriteNeighborhoodOptionsFrame")
    end

    local function finalizeClose()
        if FN.Debug and FN.Debug.IsEnabled and FN.Debug:IsEnabled() and FN.Debug.EndSession then
            FN.Debug:EndSession()
        end
    end

    quick:SetScript("OnHide", function(self)
        SaveFramePosition(self)
        if FN._rebuildingOptionsFrame then return end
        finalizeClose()
        C_Timer.After(0, function()
            if FN.UI and FN.UI.CreateQuickOptions then
                FN.UI.CreateQuickOptions()
            end
        end)
    end)

    local ui = {}
    local y = 12
    local neighStrings = {}
    for _, item in ipairs(BuildNeighborhoodOptions()) do
        table.insert(neighStrings, item.text)
    end
    local CONTROL_W = math.max(CalcDropDownWidth(quick, neighStrings), 180)
    local showDebugTools = FN.Debug and FN.Debug.IsEnabled and FN.Debug:IsEnabled() or false

    local function NewRow(height)
        local row = CreateFrame("Frame", nil, quick)
        row:SetPoint("TOPLEFT", quick, "TOPLEFT", 0, -y)
        row:SetPoint("TOPRIGHT", quick, "TOPRIGHT", 0, -y)
        row:SetHeight(height)
        y = y + height + GAP_BETWEEN_ROWS
        return row
    end

    local function AddTitle(text)
        local fs = quick:CreateFontString(nil, "ARTWORK", "GameFontNormal")
        fs:SetPoint("TOP", quick, "TOP", 0, -y)
        fs:SetWidth(quick:GetWidth() - PAD_L - PAD_R)
        fs:SetJustifyH("CENTER")
        fs:SetText(text or "")
        y = y + fs:GetStringHeight() + GAP_AFTER_TITLE
    end

    local function AddText(text)
        local fs = quick:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
        fs:SetPoint("TOPLEFT", quick, "TOPLEFT", PAD_L, -y)
        fs:SetWidth(quick:GetWidth() - PAD_L - PAD_R)
        fs:SetJustifyH("LEFT")
        fs:SetText(text or "")
        y = y + fs:GetStringHeight() + GAP_AFTER_TEXT
    end

    local function AddHeader(text)
        y = y + GAP_BETWEEN_GROUP
        local fs = quick:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge")
        fs:SetPoint("TOPLEFT", quick, "TOPLEFT", HEADER_X, -y)
        fs:SetText(text or "")
        y = y + fs:GetStringHeight()
        local line1 = CreateFrame("Frame", nil, quick, "BackdropTemplate")
        line1:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8X8" })
        line1:SetBackdropColor(0, 0.44, 0.87, 0.60)
        line1:SetHeight(1)
        line1:SetPoint("TOPLEFT", quick, "TOPLEFT", PAD_L, -y - 6)
        line1:SetPoint("TOPRIGHT", quick, "TOPRIGHT", -PAD_R, -y - 6)
        local line2 = CreateFrame("Frame", nil, quick, "BackdropTemplate")
        line2:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8X8" })
        line2:SetBackdropColor(0, 0.44, 0.87, 0.20)
        line2:SetHeight(1)
        line2:SetPoint("TOPLEFT", line1, "BOTTOMLEFT", 0, -1)
        line2:SetPoint("TOPRIGHT", line1, "BOTTOMRIGHT", 0, -1)
        y = y + GAP_AFTER_HEADER
    end

    local function AddDropDown(name, labelText)
        local row = NewRow(H_DROPDOWN)
        local label = row:CreateFontString(nil, "ARTWORK", "GameFontNormal")
        label:SetText(labelText or "")
        label:SetPoint("LEFT", row, "LEFT", LABEL_X, 0)
        label:SetJustifyH("LEFT")
        local dd = FN.UI.Dropdown:Create(row, name, CONTROL_W, {maxVisible = 18})
        dd:SetPoint("TOPLEFT", row, "TOPLEFT", DD_FRAME_X, -8)
        dd._row = row
        dd._label = label
        return dd
    end

    local logo = quick:CreateTexture(nil, "ARTWORK")
logo:SetTexture("Interface\\AddOns\\FavoriteNeighborhood\\Images\\Logo\\FN_Logo.tga")
logo:SetSize(400, 125)
logo:SetPoint("TOP", quick, "TOP", 0, -12)

y = y + 140

    AddText(L["Choose your all-time favorite neighborhood.\nAuto switch can manage the active neighborhood for your whole account."])

    AddHeader(L["Account"])
    ui.ddGlobal = AddDropDown("FN_Q_GlobalDD", L["All-time Favorite"])

    local rowTarget = NewRow(22)
    ui.targetText = rowTarget:CreateFontString(nil, "ARTWORK", "GameFontNormal")
    ui.targetText:SetPoint("CENTER", rowTarget, "CENTER", 0, 0)
    ui.targetText:SetText(L["Collecting for: None"])

    local rowProgress = NewRow(110)
    ui.progressBar = CreateProgressBar(rowProgress, 380, 20)
    ui.progressBar:SetPoint("TOP", rowProgress, "TOP", 0, -6)
    ui.progressBar:SetProgressInfo({ percent = 0 })

    ui.autoSwitchLine = CreateFrame("Frame", nil, rowProgress)
    ui.autoSwitchLine:SetPoint("TOPLEFT", rowProgress, "TOPLEFT", 0, -82)
    ui.autoSwitchLine:SetPoint("TOPRIGHT", rowProgress, "TOPRIGHT", 0, -62)
    ui.autoSwitchLine:SetHeight(20)

    ui.autoSwitchCheck = CreateFrame("CheckButton", nil, ui.autoSwitchLine, "UICheckButtonTemplate")
    ui.autoSwitchCheck:ClearAllPoints()
    ui.autoSwitchCheck:SetPoint("LEFT", ui.autoSwitchLine, "LEFT", 28, 0)

    ui.autoSwitchLabel = ui.autoSwitchLine:CreateFontString(nil, "ARTWORK", "GameFontNormal")
    ui.autoSwitchLabel:ClearAllPoints()
    ui.autoSwitchLabel:SetPoint("LEFT", ui.autoSwitchCheck, "RIGHT", 2, 0)
    ui.autoSwitchLabel:SetText(L["Auto switch for account"])

    if showDebugTools then
        AddHeader(L["Debug"])
        ui.ddDebugPreview = AddDropDown("FN_Q_DebugPreviewDD", L["Preview"])
        ui.ddDebugProgress = AddDropDown("FN_Q_DebugProgressDD", L["Bar fill"])
        local rowDebugAnim = NewRow(24)
        ui.debugAnimCheck = CreateFrame("CheckButton", nil, rowDebugAnim, "UICheckButtonTemplate")
        ui.debugAnimCheck:SetPoint("LEFT", rowDebugAnim, "LEFT", DD_FRAME_X, 0)
        ui.debugAnimCheck.text = rowDebugAnim:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
        ui.debugAnimCheck.text:SetPoint("LEFT", ui.debugAnimCheck, "RIGHT", 4, 0)
        ui.debugAnimCheck.text:SetText(L["Animated (20%% steps)"])
    end

    local rowClose = NewRow(34)
    ui.closeBtn = CreateBlueButton(rowClose, L["Close"], 140, 22)
    ui.closeBtn:SetPoint("CENTER", rowClose, "CENTER", -10, 0)

    quick:SetHeight(y + 20)

    local function Refresh()
        if FN._refreshingOptions then return end
        FN._refreshingOptions = true
        local debugEnabled = FN.Debug and FN.Debug.IsEnabled and FN.Debug:IsEnabled() or false
        local neighItems = BuildNeighborhoodOptions()

        local displayGlobal = FN_DB.global
        local autoEnabled = FN_DB.config and FN_DB.config.autoSwitchGlobal or false
        local state = FN.AutoSwitch and FN.AutoSwitch.EvaluateState and FN.AutoSwitch:EvaluateState() or nil

        if autoEnabled then
            FN._pendingManualFavoriteDisplayID = nil
        end

        local displayID = state and state.displayID or nil
        if not autoEnabled and FN._pendingManualFavoriteDisplayID then
            displayID = FN._pendingManualFavoriteDisplayID
        end
        if not displayID then
            displayID = displayGlobal
        end
        if not displayID and neighItems[1] and neighItems[1].value then
            displayID = neighItems[1].value
        end

        local pdata = FN.GetProgressData and FN.GetProgressData(displayID) or nil
        local liveProgressInfo = {
            id = displayID,
            name = FN.ResolveNeighborhoodName(displayID),
            percent = pdata and pdata.percent or 0,
            currentProgress = pdata and pdata.currentProgress or nil,
            maxProgress = pdata and pdata.maxProgress or nil,
        }
        local displayProgressInfo = (FN.Debug and FN.Debug.GetDisplayProgressInfo and FN.Debug:GetDisplayProgressInfo(liveProgressInfo)) or liveProgressInfo

        local allComplete = state and state.allComplete or false
        local showRecommended = false

        if state then
            if state.autoSwitchEnabled then
                -- Do not fight the working auto-switch logic with a separate UI recommendation.
                -- When auto-switch is enabled, only show completion. The switch itself already
                -- decides whether a move is needed.
                showRecommended = false
            else
                -- In manual mode the warning should follow the progress bar / displayed neighborhood.
                -- This keeps the UI consistent and prevents "Switch recommended" from showing on
                -- neighborhoods that are still in progress (for example 24%).
                showRecommended = displayID and FN.AutoSwitch and FN.AutoSwitch.ShouldShowSwitchRecommended
                    and FN.AutoSwitch:ShouldShowSwitchRecommended(displayID, state and state.knownIDs or nil)
                    or false
            end
        end

        if allComplete then
            displayProgressInfo.warningText = L["All neighborhoods completed"]
            displayProgressInfo.warningColor = {1.0, 0.82, 0.0}
        elseif showRecommended then
            displayProgressInfo.warningText = L["Switch recommended"]
            displayProgressInfo.warningColor = {1.0, 0.25, 0.25}
        else
            displayProgressInfo.warningText = nil
            displayProgressInfo.warningColor = nil
        end

        ui.progressBar:SetProgressInfo(displayProgressInfo)
        ui.targetText:SetText(string.format(L["Collecting for: %s"], tostring(FN.ResolveNeighborhoodName(displayID))))

        if not autoEnabled and FN._pendingManualFavoriteDisplayID then
            local activeNow = FN.GetActiveNeighborhood and FN.GetActiveNeighborhood() or nil
            if tostring(activeNow or "") == tostring(FN._pendingManualFavoriteDisplayID or "") then
                FN._pendingManualFavoriteDisplayID = nil
            end
        end

        ui.ddGlobal:SetItems(neighItems)
        ui.ddGlobal:SetSelectedValue(displayGlobal, true)
        if not displayGlobal and neighItems[1] then
            ui.ddGlobal:SetText(neighItems[1].text)
        end

        ui.autoSwitchCheck:SetChecked(autoEnabled)
        if autoEnabled then
            ui.ddGlobal:Disable()
            ui.ddGlobal.text:SetAlpha(0.55)
        else
            ui.ddGlobal:Enable()
            ui.ddGlobal.text:SetAlpha(1)
        end

        ui.ddGlobal.onSelect = function(item)
            local picked = item and item.value or nil
            FN_DB.global = picked

            local autoEnabledNow = FN_DB.config and FN_DB.config.autoSwitchGlobal or false
            if not autoEnabledNow then
                FN._pendingManualFavoriteDisplayID = picked
                if debugEnabled then
                    if FN.Debug and FN.Debug.SetActiveNeighborhoodOverride then
                        FN.Debug:SetActiveNeighborhoodOverride(picked)
                    end
                else
                    if FN.SetNeighborhood then
                        FN.SetNeighborhood(picked)
                    end
                    if FN.RequestNeighborhoodProgress then
                        FN.RequestNeighborhoodProgress(picked)
                    end
                end
            else
                FN._pendingManualFavoriteDisplayID = nil
            end

            Refresh()
            if FN.AutoSwitch and FN.AutoSwitch.HandleFavoriteChanged then
                FN.AutoSwitch:HandleFavoriteChanged(FN.GetActiveNeighborhood and FN.GetActiveNeighborhood() or picked)
            end
        end

        if ui.ddDebugPreview and FN.Debug and FN.Debug.GetPreviewItems then
            local debugCfg = FN.Debug:GetConfig()
            local selectedDebugID = (FN.Debug.GetActiveNeighborhoodOverride and FN.Debug:GetActiveNeighborhoodOverride()) or (neighItems[1] and neighItems[1].value) or nil
            local selectedDebugCfg = (FN.Debug.GetNeighborhoodConfig and selectedDebugID) and FN.Debug:GetNeighborhoodConfig(selectedDebugID) or nil

            ui.ddDebugPreview:SetItems(FN.Debug:GetPreviewItems())
            ui.ddDebugPreview:SetSelectedValue(debugCfg and debugCfg.preview or "LIVE", true)
            ui.ddDebugPreview.onSelect = function(item)
                FN.Debug:SetPreviewMode(item and item.value or "LIVE")
                Refresh()
            end
            if debugEnabled then ui.ddDebugPreview:Enable() else ui.ddDebugPreview:Disable() end
            ui.ddDebugPreview.text:SetAlpha(debugEnabled and 1 or 0.55)

            ui.ddDebugProgress:SetItems(FN.Debug:GetProgressItems())
            ui.ddDebugProgress:SetSelectedValue(selectedDebugCfg and selectedDebugCfg.progress or 40, true)
            ui.ddDebugProgress.onSelect = function(item)
                FN.Debug:SetProgress(item and item.value or 0)
                Refresh()
            end
            local allowManual = debugEnabled and debugCfg and debugCfg.preview == "DEBUG" and selectedDebugCfg and not selectedDebugCfg.animated
            if allowManual then ui.ddDebugProgress:Enable() else ui.ddDebugProgress:Disable() end
            ui.ddDebugProgress.text:SetAlpha(allowManual and 1 or 0.55)
            local allowState = debugEnabled and debugCfg and debugCfg.preview == "DEBUG" and selectedDebugCfg

            ui.debugAnimCheck:SetChecked(selectedDebugCfg and selectedDebugCfg.animated or false)
            if allowState then
                ui.debugAnimCheck:Enable(); ui.debugAnimCheck:SetAlpha(1)
                ui.debugAnimCheck.text:SetTextColor(1,1,1)
            else
                ui.debugAnimCheck:Disable(); ui.debugAnimCheck:SetAlpha(0.55)
                ui.debugAnimCheck.text:SetTextColor(0.55,0.55,0.55)
            end
        end
        FN._refreshingOptions = false
    end

    ui.closeBtn:SetScript("OnClick", function()
        if FN.Debug and FN.Debug.EndSession then FN.Debug:EndSession() end
        quick:Hide()
    end)

    ui.autoSwitchCheck:SetScript("OnClick", function(self)
        FN_DB.config = FN_DB.config or {}
        FN_DB.config.autoSwitchGlobal = self:GetChecked() and true or false

        -- Toggling auto-switch must not mutate stored progress or force a neighborhood/progress request.
        -- It should only change the mode, optionally let the auto-switch logic evaluate on enable,
        -- and then refresh the UI from the current state.
        if FN_DB.config.autoSwitchGlobal then
            FN._pendingManualFavoriteDisplayID = nil
            if FN.AutoSwitch and FN.AutoSwitch.HandleAutoSwitchEnabled then
                FN.AutoSwitch:HandleAutoSwitchEnabled()
            end
        end

        if FN.UI.Quick.Refresh then FN.UI.Quick.Refresh() end
    end)

    if ui.debugAnimCheck then
        ui.debugAnimCheck:SetScript("OnClick", function(self)
            if FN.Debug and FN.Debug.SetAnimated then
                FN.Debug:SetAnimated(self:GetChecked() and true or false)
            end
            if FN.UI.Quick.Refresh then FN.UI.Quick.Refresh() end
        end)
    end

    FN.UI.Quick.frame = quick
    FN.UI.Quick.Refresh = Refresh
    FN.OptionsFrame = quick
    FN.UpdateOptionsFrame = Refresh
    Refresh()
end

function FN.ToggleOptionsFrame(forceState)
    if not FN.UI.Quick.frame then
        local ok, err = pcall(FN.UI.CreateQuickOptions)
        if not ok then
            if FN.Msg then FN.Msg(string.format(L["options frame error: %s"], tostring(err))) end
            return
        end
    end

    local shouldShow
    if forceState == nil then
        shouldShow = not FN.UI.Quick.frame:IsShown()
    else
        shouldShow = forceState and true or false
    end

    if not shouldShow then
        if FN.Debug and FN.Debug.EndSession then FN.Debug:EndSession() end
        if FN.UI.Quick.frame and FN.UI.Quick.frame:IsShown() then
            FN.UI.Quick.frame:Hide()
        end
        return
    end

    FN.UI.CreateQuickOptions()
    FN.UI.Quick.frame:Show()
    if FN.UI.Quick.Refresh then FN.UI.Quick.Refresh() end
end

SLASH_FAVORITENEIGHBORHOOD1 = "/fn"
SlashCmdList["FAVORITENEIGHBORHOOD"] = function(msg)
    local cmd = tostring(msg or ""):lower():match("^%s*(.-)%s*$")
    if cmd == "debug" or cmd == "debug on" or cmd == "debug off" then
        local enabled = false
        if FN.Debug and FN.Debug.SetEnabled then
            if cmd == "debug on" then
                FN.Debug:SetEnabled(true)
            elseif cmd == "debug off" then
                FN.Debug:SetEnabled(false)
            else
                FN.Debug:ToggleEnabled()
            end
            enabled = FN.Debug:IsEnabled()
            if FN.Msg then
                FN.Msg(enabled and L["debug mode enabled."] or L["debug mode disabled."])
            end
        end

        if enabled then
            if FN.ToggleOptionsFrame then
                FN.ToggleOptionsFrame(true)
            else
                FN.UI.CreateQuickOptions()
                if FN.UI.Quick and FN.UI.Quick.frame then
                    FN.UI.Quick.frame:Show()
                    if FN.UI.Quick.Refresh then FN.UI.Quick.Refresh() end
                end
            end
        else
            if FN.UI and FN.UI.Quick and FN.UI.Quick.frame and FN.UI.Quick.frame:IsShown() then
                FN.UI.CreateQuickOptions()
                if FN.UI.Quick.Refresh then FN.UI.Quick.Refresh() end
            end
        end
        return
    end

    if FN.ToggleOptionsFrame then FN.ToggleOptionsFrame() end
end
