local addonName, privateDB = ...
local HDG = _G.HousingDecorGuide

-- Module namespace
HDG_UI = {}

-- Local references
local CONFIG = privateDB.CONFIG
local L = HDG_L or {}  -- Localization
-- HDG_Data is global, so we can access it directly or via _G.HDG_Data
-- Check if HDG_Data is initialized, if not, use _G.HDG_Data
local HDG_Data = _G.HDG_Data
-- Note: privateDB.HDG_Data might be nil if initialized later, so best to rely on _G

-- UI State
local mainFrame
local activeTab = "recipes"

-- Orc quotes for footer (changes on PLAYER_ENTERING_WORLD)
local ORC_QUOTES = {
    "Zug zug.",
    "Lok'tar ogar!",
    "Me smash!",
    "Work is da poop!",
    "Blood and Thunder!",
    "Dabu.",
    "Time to kill?",
    "For the Horde!",
    "Me busy!",
    "Axe hungry.",
    "Crush them all!",
    "Why you poke me?",
    "Strength and Honor.",
    "Me not that kind of orc.",
    "You weak. Me strong.",
    "More wood? Ugh.",
    "Me hit it now?",
    "Victory or death!",
    "Small talk is for elves.",
    "Something need doing?",
}
local currentQuote = ORC_QUOTES[math.random(#ORC_QUOTES)]

-- Use centralized Data() accessor from UIHelpers
local function Data()
    return HDG_UIHelpers.Data()
end

-- ============================================================================
-- HELPER FUNCTIONS
-- ============================================================================

-- Use centralized ClearScrollContent from UIHelpers
local function ClearScrollContent(scrollContent)
    HDG_UIHelpers.ClearScrollContent(scrollContent)
end

-- Use centralized GetProgressBarColor from UIHelpers
local function GetProgressBarColor(percentage)
    return HDG_UIHelpers.GetProgressBarColor(percentage)
end

local function CreateProgressBar(parent, have, needed, xPos, yPos, showText)
    if not CONFIG.PROGRESS_BAR.SHOW_BARS then return nil end

    local bar = CreateFrame("StatusBar", nil, parent, "BackdropTemplate")
    bar:SetSize(CONFIG.PROGRESS_BAR.WIDTH, CONFIG.PROGRESS_BAR.HEIGHT)
    bar:SetPoint("RIGHT", xPos or -60, yPos or 0)
    bar:SetStatusBarTexture(CONFIG.TEXTURES.STATUSBAR)
    bar:SetMinMaxValues(0, needed)
    bar:SetValue(math.min(have, needed))

    local percentage = needed > 0 and (have / needed * 100) or 0
    bar:SetStatusBarColor(GetProgressBarColor(percentage))

    local bg = bar:CreateTexture(nil, "BACKGROUND")
    bg:SetAllPoints(bar)
    bg:SetTexture(CONFIG.TEXTURES.STATUSBAR)
    bg:SetVertexColor(unpack(CONFIG.COLORS.PROGRESS_BG))

    if showText then
        local text = bar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
        text:SetPoint("CENTER", 0, 0)
        text:SetJustifyH("CENTER")
        if have >= needed then
            local extra = have - needed
            text:SetText(extra > 0 and string.format("|cFFFFFFFF%d/%d+%d|r", needed, needed, extra) or
                string.format("|cFFFFFFFF%d/%d|r", needed, needed))
        else
            text:SetText(string.format("|cFFFFFFFF%d/%d|r", have, needed))
        end
        text:SetFont(text:GetFont(), 10, "")
        bar.text = text
    end

    return bar
end

-- Helper: Count known recipes
function HDG_UI.CountKnownRecipes(profData)
    if not profData or not profData.items then return 0 end

    local knownCount = 0
    local dataModule = Data()
    local knowsBySpellID = dataModule.PlayerKnowsRecipeBySpellID
    local knowsByName = dataModule.PlayerKnowsRecipe

    for _, recipe in ipairs(profData.items) do
        local isKnown = false
        if recipe.spell and recipe.spell > 0 then
            isKnown = knowsBySpellID and knowsBySpellID(recipe.spell)
        elseif knowsByName then
            isKnown = knowsByName(recipe.name)
        end

        if isKnown then
            knownCount = knownCount + 1
        end
    end
    return knownCount
end

-- CreateLumberRow is now provided by HDG_UIHelpers.CreateLumberRow

-- ============================================================================
-- UI CREATION
-- ============================================================================

function HDG_UI.CreateUI()
    if mainFrame then return end

    mainFrame = CreateFrame("Frame", "HDG_MainFrame", UIParent, "BackdropTemplate")
    mainFrame:SetSize(CONFIG.MAIN_FRAME_WIDTH, CONFIG.MAIN_FRAME_HEIGHT)

    -- Restore saved position or default to center
    local savedPos = HDG_Settings.framePosition
    if savedPos and savedPos[1] and savedPos[3] and savedPos[4] then
        mainFrame:SetPoint(savedPos[1], UIParent, savedPos[2] or savedPos[1], savedPos[3], savedPos[4])
    else
        mainFrame:SetPoint("CENTER")
    end

    mainFrame:SetBackdrop({
        bgFile = CONFIG.TEXTURES.WHITE_BG,
        edgeFile = CONFIG.TEXTURES.WHITE_BG,  -- Solid 2px border (consistent with panels)
        tile = false,
        edgeSize = 1,
        insets = { left = 0, right = 0, top = 0, bottom = 0 }
    })
    mainFrame:SetBackdropColor(unpack(CONFIG.COLORS.MAIN_BG))
    mainFrame:SetBackdropBorderColor(unpack(CONFIG.COLORS.MAIN_BORDER))
    mainFrame:SetFrameStrata("HIGH")
    mainFrame:SetMovable(true)
    mainFrame:EnableMouse(true)
    mainFrame:RegisterForDrag("LeftButton")
    mainFrame:SetScript("OnDragStart", mainFrame.StartMoving)
    mainFrame:SetScript("OnDragStop", function(self)
        self:StopMovingOrSizing()
        local point, _, relativePoint, xOfs, yOfs = self:GetPoint()
        HDG_Settings.framePosition = { point, relativePoint, xOfs, yOfs }
    end)
    mainFrame:SetScript("OnShow", function()
        -- Update pinned lumber widget's expand button when main window opens
        if HDG_LumberTracker and HDG_LumberTracker.IsPinnedVisible and HDG_LumberTracker.IsPinnedVisible() then
            local pinnedFrame = HDG_LumberTracker.GetPinnedFrame()
            if pinnedFrame and pinnedFrame.expandBtn and pinnedFrame.expandBtn.UpdateState then
                pinnedFrame.expandBtn.UpdateState()
            end
        end
    end)
    mainFrame:SetScript("OnHide", function()
        -- Update pinned lumber widget's expand button when main window closes
        if HDG_LumberTracker and HDG_LumberTracker.IsPinnedVisible and HDG_LumberTracker.IsPinnedVisible() then
            local pinnedFrame = HDG_LumberTracker.GetPinnedFrame()
            if pinnedFrame and pinnedFrame.expandBtn and pinnedFrame.expandBtn.UpdateState then
                pinnedFrame.expandBtn.UpdateState()
            end
        end
    end)
    mainFrame:Hide()
    table.insert(UISpecialFrames, "HDG_MainFrame")

    -- Initialize Trainers Module (needs mainFrame)
    if _G.HDG_Trainers and _G.HDG_Trainers.Init then
        _G.HDG_Trainers:Init(CONFIG, mainFrame, Data().PlayerHasProfession)
    end


    -- Title banner texture (v2.0.5, resized v2.1.1)
    -- NOTE: Title MUST remain "Housing Decor Crafting Guide" to match Curseforge addon name
    mainFrame.titleTexture = mainFrame:CreateTexture(nil, "ARTWORK")
    mainFrame.titleTexture:SetTexture(CONFIG.ADDON_TEXTURES.TITLE_BANNER)
    mainFrame.titleTexture:SetSize(408, CONFIG.BANNER_HEIGHT)
    mainFrame.titleTexture:SetPoint("TOPLEFT", 8, -4)

    -- Version text (bottom left of window)
    mainFrame.titleText = mainFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    mainFrame.titleText:SetPoint("BOTTOMLEFT", 10, 6)
    mainFrame.titleText:SetText(string.format("|cFF64748bv%s|r", privateDB.ADDON_VERSION))

    -- Progress bar removed - totals now shown in profession tabs TOTAL row

    -- Close button (custom texture)
    local closeBtn = CreateFrame("Button", nil, mainFrame)
    closeBtn:SetSize(CONFIG.ICON_SIZE, CONFIG.ICON_SIZE)
    closeBtn:SetPoint("TOPRIGHT", -6, -2)
    local closeIcon = closeBtn:CreateTexture(nil, "ARTWORK")
    closeIcon:SetAllPoints()
    closeIcon:SetTexture(CONFIG.ADDON_TEXTURES.CLOSE)
    closeBtn:SetScript("OnClick", function() mainFrame:Hide() end)
    closeBtn:SetScript("OnEnter", function(self)
        closeIcon:SetVertexColor(1.2, 1.2, 1.2, 1)  -- Brighten on hover
    end)
    closeBtn:SetScript("OnLeave", function(self)
        closeIcon:SetVertexColor(1, 1, 1, 1)  -- Normal color
    end)

    -- Refresh button (custom texture)
    local refreshBtn = CreateFrame("Button", nil, mainFrame)
    refreshBtn:SetSize(CONFIG.ICON_SIZE, CONFIG.ICON_SIZE)
    refreshBtn:SetPoint("TOPRIGHT", closeBtn, "TOPLEFT", -4, 0)
    local refreshIcon = refreshBtn:CreateTexture(nil, "ARTWORK")
    refreshIcon:SetAllPoints()
    refreshIcon:SetTexture(CONFIG.ADDON_TEXTURES.REFRESH)
    refreshBtn:SetScript("OnClick", function()
        Data().ClearCaches()
        HDG_Data.CollectCharacterProfessions()  -- Re-scan profession skill levels

        -- Refresh all tabs
        if privateDB.HDG_RecipesTab then privateDB.HDG_RecipesTab:Refresh() end
        if privateDB.HDG_GoblinTab and privateDB.HDG_GoblinTab.Refresh then privateDB.HDG_GoblinTab.Refresh() end
        if HDG_DecorPreview and HDG_DecorPreview.Refresh then HDG_DecorPreview:Refresh() end
        if _G.HDG_Trainers and _G.HDG_Trainers.Refresh then _G.HDG_Trainers:Refresh() end
        if privateDB.HDG_AltsTab and privateDB.HDG_AltsTab.Refresh then privateDB.HDG_AltsTab.Refresh() end
        if privateDB.HDG_DataTab and privateDB.HDG_DataTab.RefreshFarmingDisplay then privateDB.HDG_DataTab.RefreshFarmingDisplay() end

        HDG_UI.RefreshMaterials()
        HDG_UI.RefreshLumberTracking()

        print("|cFF00FF00Housing Decor Guide:|r Refreshed all tabs and cleared caches")
    end)
    refreshBtn:SetScript("OnEnter", function(self)
        refreshIcon:SetVertexColor(1.2, 1.2, 1.2, 1)  -- Brighten on hover
        GameTooltip:SetOwner(self, HDG_UIHelpers.TOOLTIP_ANCHOR)
        GameTooltip:AddLine("|cFFfcd34dRefresh|r", 1, 1, 1)
        GameTooltip:AddLine("Clears caches and refreshes all tabs", 0.8, 0.8, 0.8)
        GameTooltip:AddLine(" ")
        GameTooltip:AddLine("Refreshes:", 1, 1, 1)
        GameTooltip:AddLine("  Crafting, Goblin, Preview, Trainers,", 0.7, 0.7, 0.7)
        GameTooltip:AddLine("  Alts, Data, Materials, Lumber", 0.7, 0.7, 0.7)
        GameTooltip:AddLine(" ")
        GameTooltip:AddLine("Use if:", 1, 1, 1)
        GameTooltip:AddLine("  - New recipe not showing", 0.7, 0.7, 0.7)
        GameTooltip:AddLine("  - Item counts seem wrong", 0.7, 0.7, 0.7)
        GameTooltip:AddLine("  - Collection status is stale", 0.7, 0.7, 0.7)
        GameTooltip:Show()
    end)
    refreshBtn:SetScript("OnLeave", function()
        refreshIcon:SetVertexColor(1, 1, 1, 1)  -- Normal color
        GameTooltip:Hide()
    end)
    mainFrame.refreshBtn = refreshBtn

    -- Help button (custom texture) - opens Config tab with How to Use guide
    local helpBtn = CreateFrame("Button", nil, mainFrame)
    helpBtn:SetSize(CONFIG.ICON_SIZE, CONFIG.ICON_SIZE)
    helpBtn:SetPoint("TOPRIGHT", refreshBtn, "TOPLEFT", -4, 0)
    local helpIcon = helpBtn:CreateTexture(nil, "ARTWORK")
    helpIcon:SetAllPoints()
    helpIcon:SetTexture(CONFIG.ADDON_TEXTURES.HELP)
    helpBtn:SetScript("OnClick", function()
        -- Switch to Config tab
        HDG_Settings.activeTab = "config"
        HDG_UI.UpdateTabs()
    end)
    helpBtn:SetScript("OnEnter", function(self)
        helpIcon:SetVertexColor(1.2, 1.2, 1.2, 1)  -- Brighten on hover
        GameTooltip:SetOwner(self, HDG_UIHelpers.TOOLTIP_ANCHOR)
        GameTooltip:AddLine("|cFFfcd34dHelp|r", 1, 1, 1)
        GameTooltip:AddLine("Opens the How to Use guide", 0.8, 0.8, 0.8)
        GameTooltip:Show()
    end)
    helpBtn:SetScript("OnLeave", function()
        helpIcon:SetVertexColor(1, 1, 1, 1)  -- Normal color
        GameTooltip:Hide()
    end)
    mainFrame.helpBtn = helpBtn

    -- Title row separator line
    local titleSeparator = mainFrame:CreateTexture(nil, "ARTWORK")
    titleSeparator:SetHeight(1)
    titleSeparator:SetPoint("TOPLEFT", 8, -(CONFIG.BANNER_HEIGHT + 2))
    titleSeparator:SetPoint("TOPRIGHT", -8, -(CONFIG.BANNER_HEIGHT + 2))
    titleSeparator:SetColorTexture(0.4, 0.4, 0.4, 0.6)

    -- Tab System
    local tabContainer = CreateFrame("Frame", nil, mainFrame)
    tabContainer:SetPoint("TOPLEFT", 15, -(CONFIG.BANNER_HEIGHT + 5))
    tabContainer:SetPoint("TOPRIGHT", -15, -(CONFIG.BANNER_HEIGHT + 5))
    tabContainer:SetHeight(CONFIG.TAB_HEIGHT)
    mainFrame.tabContainer = tabContainer

    local tabData = {
        { name = L["TAB_RECIPES"] or "Recipes",   key = "recipes",    side = "left", fullButton = CONFIG.ADDON_TEXTURES.TAB_RECIPES },
        { name = L["TAB_GOBLIN"] or "Goblin",     key = "goblin",     side = "left", fullButton = CONFIG.ADDON_TEXTURES.TAB_GOBLIN },
        { name = L["TAB_PREVIEW"] or "Preview",   key = "preview",    side = "left", fullButton = CONFIG.ADDON_TEXTURES.TAB_PREVIEW },
        { name = L["TAB_TRAINERS"] or "Trainers", key = "trainers",   side = "left", fullButton = CONFIG.ADDON_TEXTURES.TAB_TRAINERS },
        { name = L["TAB_ALTS"] or "Alts",         key = "characters", side = "left", fullButton = CONFIG.ADDON_TEXTURES.TAB_ALTS },
        { name = L["TAB_DATA"] or "Data",         key = "data",       side = "right", fullButton = CONFIG.ADDON_TEXTURES.TAB_DATA },
        { name = L["TAB_CONFIG"] or "Config",     key = "config",     side = "right", fullButton = CONFIG.ADDON_TEXTURES.TAB_CONFIG }
    }

    local tabs = {}
    local leftOffset = 0
    local rightOffset = 2
    for i, tabInfo in ipairs(tabData) do
        local tabBar

        if tabInfo.fullButton then
            -- Full button mode: use plain Frame (no backdrop template)
            tabBar = CreateFrame("Frame", nil, tabContainer)
            tabBar:SetSize(95, CONFIG.TAB_HEIGHT - 2)

            if tabInfo.side == "right" then
                tabBar:SetPoint("RIGHT", -rightOffset, 0)
                rightOffset = rightOffset + 97
            else
                tabBar:SetPoint("LEFT", leftOffset, 0)
                leftOffset = leftOffset + 97
            end

            tabBar.key = tabInfo.key
            tabBar.isFullButton = true

            local fullButtonTex = tabBar:CreateTexture(nil, "ARTWORK")
            fullButtonTex:SetAllPoints()
            fullButtonTex:SetTexture(tabInfo.fullButton)
            tabBar.fullButtonTex = fullButtonTex
            tabBar.icon = nil
            tabBar.text = nil
        else
            -- Normal mode: StatusBar with icon + text
            tabBar = CreateFrame("StatusBar", nil, tabContainer, "BackdropTemplate")
            tabBar:SetSize(95, CONFIG.TAB_HEIGHT - 2)

            if tabInfo.side == "right" then
                tabBar:SetPoint("RIGHT", -rightOffset, 0)
                rightOffset = rightOffset + 97
            else
                tabBar:SetPoint("LEFT", leftOffset, 0)
                leftOffset = leftOffset + 97
            end

            tabBar.key = tabInfo.key
            tabBar.isFullButton = false

            tabBar:SetStatusBarTexture(CONFIG.TEXTURES.STATUSBAR)
            tabBar:SetStatusBarColor(unpack(HDG_UIHelpers.STATE_INACTIVE))
            tabBar:SetMinMaxValues(0, 1)
            tabBar:SetValue(1)  -- Full bar for solid color

            -- Background/backdrop with 1px solid dim teal border
            tabBar:SetBackdrop({
                bgFile = CONFIG.TEXTURES.WHITE_BG,
                edgeFile = CONFIG.TEXTURES.WHITE_BG,
                tile = false,
                edgeSize = 1,
                insets = { left = 0, right = 0, top = 0, bottom = 0 }
            })
            tabBar:SetBackdropColor(0.15, 0.15, 0.2, 0.3)  -- Dark background
            tabBar:SetBackdropBorderColor(unpack(HDG_UIHelpers.BORDER_COLOR_FAINT))  -- Dim teal border

            -- Tab icon
            local icon = tabBar:CreateTexture(nil, "OVERLAY")
            icon:SetSize(CONFIG.ICON_SIZE, CONFIG.ICON_SIZE)
            icon:SetPoint("LEFT", 2, 0)
            icon:SetTexture(tabInfo.icon)
            tabBar.icon = icon

            -- Tab text
            tabBar.text = tabBar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
            tabBar.text:SetPoint("LEFT", icon, "RIGHT", 4, 0)
            tabBar.text:SetText(tabInfo.name)
            tabBar.text:SetFont(tabBar.text:GetFont(), 11, "")
            tabBar.text:SetShadowOffset(0, 0)
        end

        -- Create invisible button overlay for click handling
        local clickButton = CreateFrame("Button", nil, tabBar)
        clickButton:SetAllPoints(tabBar)
        clickButton:RegisterForClicks("LeftButtonUp")
        clickButton:SetScript("OnClick", function()
            HDG_Settings.activeTab = tabInfo.key
            HDG_UI.UpdateTabs()
        end)

        -- Tooltip (localized descriptions)
        local tooltipTexts = {
            recipes = L["TIP_TAB_RECIPES"] or "View all housing decor recipes",
            goblin = L["TIP_TAB_GOBLIN"] or "Profit analysis - find the most profitable crafts",
            preview = L["TIP_TAB_PREVIEW"] or "Preview decorations",
            trainers = L["TIP_TAB_TRAINERS"] or "Find recipe trainers",
            characters = L["TIP_TAB_ALTS"] or "View profession data across all characters",
            data = L["TIP_TAB_DATA"] or "View farming history and statistics",
            config = L["TIP_TAB_CONFIG"] or "Configure addon settings and minimap icon"
        }

        clickButton:SetScript("OnEnter", function(self)
            -- Brighten on hover (only if not active)
            if HDG_Settings.activeTab ~= tabInfo.key then
                if tabBar.isFullButton then
                    if tabBar.fullButtonTex then tabBar.fullButtonTex:SetAlpha(0.95) end
                else
                    tabBar:SetStatusBarColor(unpack(HDG_UIHelpers.STATE_HOVER))
                    if tabBar.icon then tabBar.icon:SetAlpha(0.85) end
                end
            end

            GameTooltip:SetOwner(self, HDG_UIHelpers.TOOLTIP_ANCHOR)
            GameTooltip:AddLine("|cFFfcd34d" .. tabInfo.name .. "|r", 1, 1, 1)
            GameTooltip:AddLine(tooltipTexts[tabInfo.key], 1, 1, 1)
            GameTooltip:Show()
        end)

        clickButton:SetScript("OnLeave", function(self)
            -- Restore color based on active state
            if HDG_Settings.activeTab ~= tabInfo.key then
                if tabBar.isFullButton then
                    if tabBar.fullButtonTex then tabBar.fullButtonTex:SetAlpha(0.6) end
                else
                    tabBar:SetStatusBarColor(unpack(HDG_UIHelpers.STATE_INACTIVE))
                    if tabBar.icon then tabBar.icon:SetAlpha(0.6) end
                end
            end
            GameTooltip:Hide()
        end)

        -- Store both the bar and button for UpdateTabs function
        tabBar.statusBar = tabBar
        tabBar.button = clickButton

        table.insert(tabs, tabBar)
    end
    mainFrame.tabs = tabs

    -- Initialize tab modules
    HDG_LumberTracker.Init(mainFrame)

    -- Initialize tab modules with mainFrame reference
    if privateDB.HDG_RecipesTab then
        privateDB.HDG_RecipesTab:Init(mainFrame)
        privateDB.HDG_RecipesTab:Create()
    end

    if privateDB.HDG_AltsTab then
        privateDB.HDG_AltsTab.Init(mainFrame)
        privateDB.HDG_AltsTab.Create()
    end

    if privateDB.HDG_ConfigTab then
        privateDB.HDG_ConfigTab.Init(mainFrame)
        privateDB.HDG_ConfigTab.Create()
    end

    if privateDB.HDG_DataTab then
        privateDB.HDG_DataTab.Init(mainFrame)
        privateDB.HDG_DataTab.CreatePanel()
    end

    if privateDB.HDG_GoblinTab then
        privateDB.HDG_GoblinTab.Init(mainFrame)
        privateDB.HDG_GoblinTab.Create()
    end

    -- Trainers placeholder frame (actual content handled by HDG_Trainers module)
    HDG_UI.CreateTrainersPanel()

    -- Initialize state
    if not HDG_Settings.activeTab then HDG_Settings.activeTab = "recipes" end
    HDG_UI.UpdateTabs()

    -- Footer with random Orc quote (after version text)
    local footer = mainFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    footer:SetPoint("BOTTOMLEFT", 65, 6)  -- After version text
    footer:SetWidth(CONFIG.MAIN_FRAME_WIDTH - 330)  -- Leave room for house level on right
    footer:SetJustifyH("LEFT")
    footer:SetText(string.format("|cFF0070DEVamoose says:|r |cFFFFAA00%s|r", currentQuote))
    mainFrame.workflowFooter = footer

    -- House Level display (bottom right) - single line layout
    -- Layout: [XP bar] [XP text] [Level text]
    local houseLevelFrame = CreateFrame("Frame", nil, mainFrame)
    houseLevelFrame:SetSize(320, 20)
    houseLevelFrame:SetPoint("BOTTOMRIGHT", -10, 4)
    mainFrame.houseLevelFrame = houseLevelFrame

    -- XP text (far right)
    local xpText = houseLevelFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    xpText:SetPoint("BOTTOMRIGHT", houseLevelFrame, "BOTTOMRIGHT", 0, 0)
    xpText:SetTextColor(0.6, 0.6, 0.6)
    xpText:SetJustifyH("RIGHT")
    xpText:SetText("|cFF666666-- / -- XP|r")  -- Placeholder
    mainFrame.houseXpText = xpText

    -- House level text (to the left of XP text, same baseline)
    local houseLevelText = houseLevelFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    houseLevelText:SetPoint("BOTTOMRIGHT", xpText, "BOTTOMLEFT", -12, 0)
    houseLevelText:SetJustifyH("RIGHT")
    houseLevelText:SetText("|cFF14b8a6House Level|r |cFF888888--|r")  -- Placeholder
    mainFrame.houseLevelText = houseLevelText

    -- Tooltip on hover
    houseLevelFrame:EnableMouse(true)
    houseLevelFrame:SetScript("OnEnter", function(self)
        GameTooltip:SetOwner(self, "ANCHOR_TOP")
        GameTooltip:SetText("|cFF14b8a6House Level|r", 1, 1, 1)
        GameTooltip:AddLine("Earn House XP by collecting new decor items.", 0.7, 0.7, 0.7, true)
        GameTooltip:AddLine(" ")
        GameTooltip:AddLine("Higher house levels unlock more decoration slots.", 0.7, 0.7, 0.7, true)
        GameTooltip:Show()
    end)
    houseLevelFrame:SetScript("OnLeave", GameTooltip_Hide)

    -- Initial update
    HDG_UI.UpdateHouseLevelDisplay()

    privateDB.mainFrame = mainFrame
end

-- CreateRecipePanel, CreateShoppingPanel, CreateMaterialsPanel moved to HDG_RecipesTab.lua

function HDG_UI.CreateTrainersPanel()
    local trainersFrame = CreateFrame("Frame", nil, mainFrame, "BackdropTemplate")
    trainersFrame:SetPoint("TOPLEFT", CONFIG.SPACING.PANEL_PADDING, -(CONFIG.BANNER_HEIGHT + CONFIG.TAB_HEIGHT + 8))
    trainersFrame:SetSize(CONFIG.MAIN_FRAME_WIDTH - (CONFIG.SPACING.PANEL_PADDING * 2), CONFIG.PANEL_HEIGHT)
    trainersFrame:SetBackdrop({
        bgFile = CONFIG.TEXTURES.WHITE_BG,
        edgeFile = CONFIG.TEXTURES.WHITE_BG,
        tile = false,
        edgeSize = 1,
        insets = { left = 0, right = 0, top = 0, bottom = 0 }
    })
    trainersFrame:SetBackdropColor(unpack(CONFIG.COLORS.PANEL_BG))
    trainersFrame:SetBackdropBorderColor(unpack(HDG_UIHelpers.BORDER_COLOR))
    trainersFrame:Hide()
    mainFrame.trainersFrame = trainersFrame

    -- Trainers implementation details handled by HDG_Trainers module
end

-- CreateCharactersPanel and RefreshCharactersPanel moved to HDG_AltsTab.lua

function HDG_UI.RefreshCharactersPanel()
    -- Delegate to HDG_AltsTab module
    if privateDB.HDG_AltsTab and privateDB.HDG_AltsTab.Refresh then
        privateDB.HDG_AltsTab.Refresh()
    end
end

-- CreateConfigPanel moved to HDG_ConfigTab.lua (stub for compatibility)

function HDG_UI.UpdateTabs()
    local tabKey = HDG_Settings.activeTab

    mainFrame.recipeFrame:Hide()
    mainFrame.shoppingFrame:Hide()
    mainFrame.materialsFrame:Hide()
    mainFrame.trainersFrame:Hide()
    mainFrame.charactersFrame:Hide()
    mainFrame.configFrame:Hide()
    if mainFrame.dataFrame then mainFrame.dataFrame:Hide() end
    if mainFrame.goblinFrame then mainFrame.goblinFrame:Hide() end
    if HDG_DecorPreview then HDG_DecorPreview:Hide() end
    if HDG_Trainers then HDG_Trainers:Hide() end

    -- Update tab visuals (StatusBar-based tabs)
    for _, tab in ipairs(mainFrame.tabs or {}) do
        if tab.key == tabKey then
            -- Active tab
            if tab.isFullButton then
                -- Full button tab: brighten the texture (no StatusBar/backdrop)
                if tab.fullButtonTex then
                    tab.fullButtonTex:SetAlpha(1.0)
                end
            else
                -- Normal tab: dark teal background with white text, full opacity icon
                tab:SetStatusBarColor(unpack(HDG_UIHelpers.STATE_ACTIVE))
                tab:SetBackdropBorderColor(unpack(HDG_UIHelpers.BORDER_COLOR_BRIGHT))
                if tab.text then tab.text:SetTextColor(1.0, 1.0, 1.0, 1) end  -- White text (#ffffff)
                if tab.icon then tab.icon:SetAlpha(1.0) end  -- Full opacity for active tab
            end
        else
            -- Inactive tab
            if tab.isFullButton then
                -- Full button tab: dim the texture (no StatusBar/backdrop)
                if tab.fullButtonTex then
                    tab.fullButtonTex:SetAlpha(0.6)
                end
            else
                -- Normal tab: dark with subtle teal tint, reduced icon opacity
                tab:SetStatusBarColor(unpack(HDG_UIHelpers.STATE_INACTIVE))
                tab:SetBackdropBorderColor(unpack(HDG_UIHelpers.BORDER_COLOR_DIM))  -- Dim teal border
                if tab.text then tab.text:SetTextColor(0.7, 0.7, 0.7, 1) end  -- Dimmed text
                if tab.icon then tab.icon:SetAlpha(0.6) end  -- Reduced opacity - keeps color but recedes
            end
        end
    end

    if tabKey == "recipes" then
        if HDG_RecipesTab then
            HDG_RecipesTab:Show()
        end
    elseif tabKey == "preview" then
        if HDG_DecorPreview then
            if not HDG_DecorPreview.initialized then
                HDG_DecorPreview:Init(CONFIG, mainFrame, privateDB)
                HDG_DecorPreview.initialized = true
            end
            HDG_DecorPreview:Show()
        end
    elseif tabKey == "config" then
        mainFrame.configFrame:Show()
    elseif tabKey == "trainers" then
        if HDG_Trainers then
            if not HDG_Trainers.initialized then
                HDG_Trainers:Init(CONFIG, mainFrame, Data().PlayerHasProfession)
                HDG_Trainers.initialized = true
            end
            HDG_Trainers:Show()
        end
    elseif tabKey == "characters" then
        mainFrame.charactersFrame:Show()
        HDG_UI.RefreshCharactersPanel()
    elseif tabKey == "data" then
        local HDG_DataTab = privateDB.HDG_DataTab
        if HDG_DataTab then
            HDG_DataTab.Show()
        end
    elseif tabKey == "goblin" then
        local HDG_GoblinTab = privateDB.HDG_GoblinTab
        if HDG_GoblinTab then
            HDG_GoblinTab.Show()
        end
    end
end

-- Recipe rendering functions moved to HDG_RecipesTab.lua

function HDG_UI.RefreshUI()
    if not mainFrame or not mainFrame:IsShown() then return end

    -- Always refresh shopping list and materials (even if on different tab)
    -- This ensures materials update when adding items from Preview tab
    if privateDB.HDG_RecipesTab then
        privateDB.HDG_RecipesTab:RefreshShoppingList()
        privateDB.HDG_RecipesTab:RefreshMaterials()
    end

    -- Refresh active tab content
    local activeTab = HDG_Settings.activeTab

    if activeTab == "recipes" then
        -- Full recipes refresh when on recipes tab
        if privateDB.HDG_RecipesTab then
            privateDB.HDG_RecipesTab:RefreshRecipes()
        end
    elseif activeTab == "preview" then
        -- v2.1.5: Refresh preview tab when collection status changes
        if privateDB.HDG_DecorPreview then
            privateDB.HDG_DecorPreview:RefreshList()
        end
    elseif activeTab == "trainers" and HDG_Trainers then
        HDG_Trainers:Refresh()
    end

    HDG_UI.RefreshLumberTracking()
end

-- Update lumber layout based on pin state
-- When NOT pinned: show embedded lumber frame at bottom of shopping panel
-- When pinned: hide embedded frame, show separate floating window
function HDG_UI.UpdateLumberLayout()
    if not mainFrame then return end

    local isPinned = HDG_Settings.lumberPinned

    -- Handle embedded lumber frame visibility
    if mainFrame.lumberFrame then
        if isPinned then
            -- Hide embedded, show pinned
            mainFrame.lumberFrame:Hide()
            -- Extend scroll frame down since lumber frame is hidden
            if mainFrame.shoppingScroll then
                mainFrame.shoppingScroll:SetPoint("BOTTOMRIGHT", -26, 8)
            end
        else
            -- Show embedded, hide pinned
            mainFrame.lumberFrame:Show()
            -- Restore scroll frame position above lumber frame (dynamic based on lumber count)
            if mainFrame.shoppingScroll then
                local lumberReservedHeight = 24 + (#HDG_ExpansionData.LUMBER_ORDER * 18) + 8 + 8
                mainFrame.shoppingScroll:SetPoint("BOTTOMRIGHT", -26, lumberReservedHeight)
            end
            -- Refresh the embedded lumber content
            HDG_UI.RefreshEmbeddedLumber()
        end
    end

    -- Manage pinned widget visibility
    if isPinned then
        if HDG_LumberTracker and HDG_LumberTracker.ShowPinned then
            HDG_LumberTracker.ShowPinned()
        end
    else
        if HDG_LumberTracker and HDG_LumberTracker.HidePinned then
            HDG_LumberTracker.HidePinned()
        end
    end
end

-- Refresh embedded lumber frame content (used when not pinned)
function HDG_UI.RefreshEmbeddedLumber()
    if not mainFrame or not mainFrame.lumberContent then return end
    ClearScrollContent(mainFrame.lumberContent)

    local dataModule = Data()
    if not dataModule.CalculateShoppingListLumber then return end

    local shoppingLumber = dataModule.CalculateShoppingListLumber(HDG_Settings.shoppingList)
    local allLumber = dataModule.CalculateLumberNeeds(privateDB.professions or {})
    local expansionOrder = HDG_ExpansionData.LUMBER_ORDER
    local LUMBER_TYPES = dataModule.LUMBER_TYPES

    local yOffset = 0
    for _, lumberId in ipairs(expansionOrder) do
        local shoppingData = shoppingLumber[lumberId]
        local allData = allLumber[lumberId]
        local lumberInfo = LUMBER_TYPES[lumberId]

        -- Create row using the shared helper (no farming options for embedded panel)
        yOffset = HDG_UIHelpers.CreateLumberRow(mainFrame.lumberContent, lumberId, lumberInfo, shoppingData, allData, yOffset, nil)
    end
end

function HDG_UI.RefreshLumberTracking()
    if not mainFrame then return end

    -- Update lumber button text (in recipes tab) if it exists
    if mainFrame.lumberBtn and mainFrame.lumberBtn.UpdateText then
        mainFrame.lumberBtn.UpdateText()
    end

    -- Manage embedded vs pinned lumber display
    local isPinned = HDG_Settings.lumberPinned

    -- Refresh embedded lumber content if visible (not pinned)
    if not isPinned and mainFrame.lumberFrame and mainFrame.lumberFrame:IsShown() then
        HDG_UI.RefreshEmbeddedLumber()
    end

    -- Refresh the independent pinned widget if visible
    if HDG_LumberTracker and HDG_LumberTracker.IsPinnedVisible and HDG_LumberTracker.IsPinnedVisible() then
        HDG_LumberTracker.RefreshPinned()
    end
end

-- ============================================================================
-- DELEGATION TO HDG_RecipesTab
-- These functions delegate to HDG_RecipesTab for the actual implementation
-- ============================================================================

function HDG_UI.RefreshProfessionTabs()
    if privateDB.HDG_RecipesTab then
        privateDB.HDG_RecipesTab:RefreshProfessionTabs()
    end
end

function HDG_UI.AddToShoppingList(recipe, skipRefresh)
    if privateDB.HDG_RecipesTab then
        privateDB.HDG_RecipesTab:AddToShoppingList(recipe, skipRefresh)
    end
end

function HDG_UI.RefreshShoppingList()
    if privateDB.HDG_RecipesTab then
        privateDB.HDG_RecipesTab:RefreshShoppingList()
    end
end

function HDG_UI.RefreshMaterials()
    if privateDB.HDG_RecipesTab then
        privateDB.HDG_RecipesTab:RefreshMaterials()
    end
end

function HDG_UI.AddAllMaterialsToAuctionator()
    if privateDB.HDG_RecipesTab then
        privateDB.HDG_RecipesTab:AddAllMaterialsToAuctionator()
    end
end

-- Toggle UI
function HDG_UI.Toggle()
    if not mainFrame then HDG_UI.CreateUI() end
    if mainFrame:IsShown() then
        -- If lumber is pinned, show the independent pinned widget when closing main window
        if HDG_Settings.lumberPinned then
            HDG_LumberTracker.ShowPinned()
        end
        mainFrame:Hide()
    else
        mainFrame:Show()
        HDG_UI.RefreshUI()
    end
end

-- Open HDG with a specific profession filter (v1.9.5)
function HDG_UI.OpenWithProfession(professionName)
    if not mainFrame then HDG_UI.CreateUI() end

    -- Set the profession filter (per-character)
    if professionName then
        local HDG_RecipesTab = privateDB.HDG_RecipesTab
        if HDG_RecipesTab and HDG_RecipesTab.SetProfessionFilter then
            HDG_RecipesTab.SetProfessionFilter(professionName)
        end
    end

    -- Ensure we're on the recipes tab
    if HDG_Settings then
        HDG_Settings.activeTab = "recipes"
    end

    mainFrame:Show()
    HDG_UI.RefreshUI()
end

-- ============================================================================
-- PROFESSION WINDOW INTEGRATION (v1.9.5, v2.0.5)
-- Adds "Decor Guide" and "Filter Decor" buttons to Blizzard's profession UI
-- ============================================================================

local professionButton = nil
local filterDecorButton = nil
local filterDecorActive = false  -- Track filter toggle state

-- Use centralized profession data from HDG_ExpansionData
local function GetCurrentProfessionName()
    if not C_TradeSkillUI.IsTradeSkillReady() then return nil end

    local professionInfo = C_TradeSkillUI.GetBaseProfessionInfo()
    if professionInfo and professionInfo.professionID then
        return HDG_ExpansionData.GetProfessionName(professionInfo.professionID)
    end
    return nil
end

-- Helper to create a teal-styled button
local function CreateTealButton(name, parent, width, text)
    local btn = CreateFrame("Button", name, parent, "BackdropTemplate")
    btn:SetSize(width, 22)
    btn:SetFrameStrata("HIGH")

    btn:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        tile = false,
        edgeSize = 1,
        insets = { left = 0, right = 0, top = 0, bottom = 0 }
    })
    btn:SetBackdropColor(0.08, 0.45, 0.42, 0.9)  -- Teal background
    btn:SetBackdropBorderColor(0.1, 0.6, 0.55, 1)  -- Lighter teal border

    local btnText = btn:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    btnText:SetPoint("CENTER", 0, 0)
    btnText:SetText("|cFFFFFFFF" .. text .. "|r")
    btn.text = btnText

    return btn
end

local function CreateProfessionButtons()
    if professionButton then return end

    -- Check if buttons are enabled in settings
    if HDG_Settings and HDG_Settings.showProfessionButtons == false then
        return
    end

    -- ProfessionsFrame is the retail profession UI
    local parentFrame = ProfessionsFrame
    if not parentFrame then return end

    -- Create "Decor Guide" button (opens HDG filtered to current profession)
    professionButton = CreateTealButton("HDG_ProfessionButton", parentFrame, 90, L["BTN_DECOR_GUIDE"] or "Decor Guide")
    professionButton:SetPoint("TOPRIGHT", parentFrame, "TOPRIGHT", -60, -1)

    professionButton:SetScript("OnClick", function()
        local profName = GetCurrentProfessionName()
        if profName and HDG_ExpansionData.GetProfessionID(profName) then
            HDG_UI.OpenWithProfession(profName)
        else
            HDG_UI.Toggle()
        end
    end)

    professionButton:SetScript("OnEnter", function(self)
        self:SetBackdropColor(0.1, 0.55, 0.5, 1)
        GameTooltip:SetOwner(self, HDG_UIHelpers.TOOLTIP_ANCHOR)
        local profName = GetCurrentProfessionName()
        if profName and HDG_ExpansionData.GetProfessionID(profName) then
            GameTooltip:AddLine("|cFFfcd34dOpen Decor Guide|r")
            GameTooltip:AddLine("Filtered to " .. profName .. " decor recipes", 0.7, 0.7, 0.7)
        else
            GameTooltip:AddLine("|cFFfcd34dOpen Decor Guide|r")
            GameTooltip:AddLine("View all housing decor recipes", 0.7, 0.7, 0.7)
        end
        GameTooltip:Show()
    end)

    professionButton:SetScript("OnLeave", function(self)
        self:SetBackdropColor(0.08, 0.45, 0.42, 0.9)
        GameTooltip:Hide()
    end)

    -- Create "Filter Decor" button (filters profession window to House Decor)
    local filterDecorLabel = L["BTN_FILTER_DECOR"] or "Filter Decor"
    filterDecorButton = CreateTealButton("HDG_FilterDecorButton", parentFrame, 85, filterDecorLabel)
    filterDecorButton:SetPoint("RIGHT", professionButton, "LEFT", -4, 0)

    -- Helper to update filter button appearance based on state
    local function UpdateFilterButtonAppearance()
        if filterDecorActive then
            -- Active state: brighter teal with gold border
            filterDecorButton:SetBackdropColor(0.1, 0.55, 0.5, 1)
            filterDecorButton:SetBackdropBorderColor(0.99, 0.83, 0.22, 1)  -- Gold border when active
            filterDecorButton.text:SetText("|cFFfcd34d" .. filterDecorLabel .. "|r")  -- Gold text
        else
            -- Inactive state: normal teal
            filterDecorButton:SetBackdropColor(0.08, 0.45, 0.42, 0.9)
            filterDecorButton:SetBackdropBorderColor(0.1, 0.6, 0.55, 1)
            filterDecorButton.text:SetText("|cFFFFFFFF" .. filterDecorLabel .. "|r")  -- White text
        end
    end

    filterDecorButton:SetScript("OnClick", function()
        if C_TradeSkillUI and C_TradeSkillUI.SetRecipeItemNameFilter then
            if filterDecorActive then
                -- Clear the filter
                C_TradeSkillUI.SetRecipeItemNameFilter("")
                filterDecorActive = false
            else
                -- Apply the filter
                C_TradeSkillUI.SetRecipeItemNameFilter("House Decor")
                filterDecorActive = true
            end
            UpdateFilterButtonAppearance()
        end
    end)

    filterDecorButton:SetScript("OnEnter", function(self)
        if not filterDecorActive then
            self:SetBackdropColor(0.1, 0.55, 0.5, 1)
        end
        GameTooltip:SetOwner(self, HDG_UIHelpers.TOOLTIP_ANCHOR)
        if filterDecorActive then
            GameTooltip:AddLine("|cFFfcd34dClear Filter|r")
            GameTooltip:AddLine("Click to show all recipes again.", 1, 1, 1, true)
        else
            GameTooltip:AddLine("|cFFfcd34dFilter House Decor|r")
            GameTooltip:AddLine("Filters recipes to show only House Decor.", 1, 1, 1, true)
            GameTooltip:AddLine(" ")
            GameTooltip:AddLine("|cFF888888Note: Select each expansion tab|r", 0.7, 0.7, 0.7, true)
            GameTooltip:AddLine("|cFF888888to see all decor recipes.|r", 0.7, 0.7, 0.7, true)
        end
        GameTooltip:Show()
    end)

    filterDecorButton:SetScript("OnLeave", function(self)
        UpdateFilterButtonAppearance()
        GameTooltip:Hide()
    end)

    -- Hook into profession frame show/update for visibility
    parentFrame:HookScript("OnShow", function()
        -- Reset filter state when window opens (filter text is cleared by WoW)
        filterDecorActive = false
        UpdateFilterButtonAppearance()

        if HDG_Settings and HDG_Settings.showProfessionButtons == false then
            if professionButton then professionButton:Hide() end
            if filterDecorButton then filterDecorButton:Hide() end
        else
            if professionButton then professionButton:Show() end
            if filterDecorButton then filterDecorButton:Show() end
        end
    end)
end

-- Function to update profession button visibility based on settings
function HDG_UI.UpdateProfessionButtonVisibility()
    if HDG_Settings and HDG_Settings.showProfessionButtons == false then
        if professionButton then professionButton:Hide() end
        if filterDecorButton then filterDecorButton:Hide() end
    else
        if professionButton then professionButton:Show() end
        if filterDecorButton then filterDecorButton:Show() end
    end
end

-- Hook into profession frame loading
local professionHookFrame = CreateFrame("Frame")
professionHookFrame:RegisterEvent("ADDON_LOADED")
professionHookFrame:RegisterEvent("TRADE_SKILL_SHOW")
professionHookFrame:SetScript("OnEvent", function(self, event, arg1)
    if event == "ADDON_LOADED" and arg1 == "Blizzard_Professions" then
        -- Blizzard profession UI loaded, create our buttons
        C_Timer.After(0.1, CreateProfessionButtons)
    elseif event == "TRADE_SKILL_SHOW" then
        -- Profession window opened, ensure buttons exist
        if ProfessionsFrame then
            CreateProfessionButtons()
        end
    end
end)

-- Function to refresh the random quote
function HDG_UI.RefreshQuote()
    currentQuote = ORC_QUOTES[math.random(#ORC_QUOTES)]
    if mainFrame and mainFrame.workflowFooter then
        mainFrame.workflowFooter:SetText(string.format("|cFF0070DEVamoose says:|r |cFFFFAA00%s|r", currentQuote))
    end
end

-- Cache for house info
local cachedHouseList = nil
local cachedHouseGUID = nil

-- Request house list (async - will fire PLAYER_HOUSE_LIST_UPDATED)
function HDG_UI.RequestHouseInfo()
    if C_Housing and C_Housing.GetPlayerOwnedHouses then
        pcall(C_Housing.GetPlayerOwnedHouses)
    end
end

-- Called when PLAYER_HOUSE_LIST_UPDATED fires
function HDG_UI.OnHouseListUpdated(houseInfoList)
    cachedHouseList = houseInfoList
    if houseInfoList and #houseInfoList > 0 then
        cachedHouseGUID = houseInfoList[1].houseGUID
        -- Now request the favor info (async - will fire HOUSE_LEVEL_FAVOR_UPDATED)
        if cachedHouseGUID and C_Housing and C_Housing.GetCurrentHouseLevelFavor then
            pcall(C_Housing.GetCurrentHouseLevelFavor, cachedHouseGUID)
        end
    else
        -- No houses - hide the display
        if mainFrame and mainFrame.houseLevelFrame then
            mainFrame.houseLevelFrame:Hide()
        end
    end
end

-- Called when HOUSE_LEVEL_FAVOR_UPDATED fires with favor data
function HDG_UI.OnHouseLevelFavorUpdated(houseLevelFavor)
    if not mainFrame or not mainFrame.houseLevelText then return end
    if not houseLevelFavor then
        mainFrame.houseLevelFrame:Hide()
        return
    end

    -- From Blizzard code: houseLevelFavor has houseGUID, houseLevel, houseFavor
    local currentLevel = houseLevelFavor.houseLevel or 1
    local currentXP = houseLevelFavor.houseFavor or 0

    -- Get max level
    local maxLevel = 50
    if C_Housing and C_Housing.GetMaxHouseLevel then
        local success, max = pcall(C_Housing.GetMaxHouseLevel)
        if success and max then maxLevel = max end
    end

    -- Get XP needed for next level (GetHouseLevelFavorForLevel returns a number directly)
    local xpForNextLevel = 0
    if currentLevel < maxLevel and C_Housing and C_Housing.GetHouseLevelFavorForLevel then
        local success, needed = pcall(C_Housing.GetHouseLevelFavorForLevel, currentLevel + 1)
        if success and needed then
            xpForNextLevel = needed
        end
    end

    -- Update level text
    if currentLevel >= maxLevel then
        mainFrame.houseLevelText:SetText(string.format("|cFF14b8a6House Level|r |cFFfcd34d%d|r |cFF888888(Max)|r", currentLevel))
    else
        mainFrame.houseLevelText:SetText(string.format("|cFF14b8a6House Level|r |cFFfcd34d%d|r", currentLevel))
    end

    -- Calculate XP progress for THIS level (not total)
    -- Need XP from previous level to current level
    local xpForCurrentLevel = 0
    if currentLevel > 0 and C_Housing and C_Housing.GetHouseLevelFavorForLevel then
        local success, needed = pcall(C_Housing.GetHouseLevelFavorForLevel, currentLevel)
        if success and needed then
            xpForCurrentLevel = needed
        end
    end

    -- Update XP text (always show current / threshold XP)
    -- At max level, xpForNextLevel will be 0, so use xpForCurrentLevel as the threshold
    local xpThreshold = xpForNextLevel > 0 and xpForNextLevel or xpForCurrentLevel
    mainFrame.houseXpText:SetText(string.format("%d / %d XP", currentXP, xpThreshold))

    mainFrame.houseLevelFrame:Show()
end

-- Legacy function for compatibility - just requests the data
function HDG_UI.UpdateHouseLevelDisplay()
    if not mainFrame or not mainFrame.houseLevelText then return end

    -- Check if C_Housing API is available
    if not C_Housing then
        if mainFrame.houseLevelFrame then
            mainFrame.houseLevelFrame:Hide()
        end
        return
    end

    -- Request house info (async)
    HDG_UI.RequestHouseInfo()
end

-- Register for PLAYER_ENTERING_WORLD to change quote and house level events
local quoteFrame = CreateFrame("Frame")
quoteFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
quoteFrame:RegisterEvent("PLAYER_HOUSE_LIST_UPDATED")
quoteFrame:RegisterEvent("HOUSE_LEVEL_FAVOR_UPDATED")
quoteFrame:RegisterEvent("HOUSE_LEVEL_CHANGED")
quoteFrame:SetScript("OnEvent", function(self, event, ...)
    if event == "PLAYER_ENTERING_WORLD" then
        HDG_UI.RefreshQuote()
        HDG_UI.UpdateHouseLevelDisplay()
    elseif event == "PLAYER_HOUSE_LIST_UPDATED" then
        -- Receives houseInfoList as first argument
        local houseInfoList = ...
        HDG_UI.OnHouseListUpdated(houseInfoList)
    elseif event == "HOUSE_LEVEL_FAVOR_UPDATED" then
        -- Receives houseLevelFavor as first argument
        local houseLevelFavor = ...
        HDG_UI.OnHouseLevelFavorUpdated(houseLevelFavor)
    elseif event == "HOUSE_LEVEL_CHANGED" then
        -- Level changed - request fresh data
        HDG_UI.UpdateHouseLevelDisplay()
    end
end)

-- Export UI module
_G.HDG_UI = HDG_UI
privateDB.RefreshUI = HDG_UI.RefreshUI
