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

-- ============================================================================
-- MAIN ADDON FILE
-- Event Handling and Entry Point
-- ============================================================================

-- Track pending craft operations for multi-craft scenarios
-- When crafting multiple items, only close window after all complete
privateDB.pendingCraftInfo = {
    itemID = nil,      -- The itemID being crafted
    remaining = 0,     -- How many crafts remaining
}

-- ============================================================================
-- STORE HELPERS
-- ============================================================================

local function GetStore()
    local hdg = _G.HousingDecorGuide
    return hdg and hdg.Store
end

local function GetState()
    local store = GetStore()
    return store and store:GetState()
end

local function Dispatch(action, payload)
    local store = GetStore()
    if store then
        store:Dispatch(action, payload)
    end
end

local function GetConfig(key)
    local state = GetState()
    return state and state.config and state.config[key]
end

local function GetUIState(key)
    local state = GetState()
    return state and state.ui and state.ui[key]
end

local function GetCraftingQueue()
    local state = GetState()
    return state and state.craftingQueue or {}
end

local function GetCharacters()
    local state = GetState()
    return state and state.characters or {}
end

-- Track craft quote frequency (respects vamooseChattiness slider)
-- Similar to lumber quote throttling in HDG_Vamoose.lua
local craftQuoteTracking = {
    craftsSinceQuote = 0,      -- Counter since last quote
    nextQuoteThreshold = 1,    -- Random threshold for next quote (1 to chattiness)
}

-- Check if a craft quote should be displayed based on chattiness setting
-- Returns true if quote should show, false otherwise
local function ShouldShowCraftQuote(chattiness)
    if chattiness <= 0 then return false end

    -- Increment craft count
    craftQuoteTracking.craftsSinceQuote = craftQuoteTracking.craftsSinceQuote + 1

    -- Check if we've hit the threshold
    if craftQuoteTracking.craftsSinceQuote >= craftQuoteTracking.nextQuoteThreshold then
        -- Reset for next quote with new random threshold
        craftQuoteTracking.craftsSinceQuote = 0
        craftQuoteTracking.nextQuoteThreshold = math.random(1, math.max(1, chattiness))
        return true
    end

    return false
end

-- Initialize Database
local initialized = HDG_Data.InitializeDatabase()
if not initialized then
    print("|cFFFF0000[Housing Decor Guide]|r Failed to initialize database! Addon will not work.")
    return
end

-- Bridge functions to expose UI methods on the global object if needed by legacy code or macros
function HDG:RefreshUI()
    HDG_UI.RefreshUI()
end

function HDG:RefreshCraftingQueue()
    HDG_UI.RefreshCraftingQueue()
end

function HDG:RefreshMaterials()
    HDG_UI.RefreshMaterials()
end

function HDG:RefreshLumberTracking()
    HDG_UI.RefreshLumberTracking()
end

function HDG:Toggle()
    HDG_UI.Toggle()
end

-- Forward declarations for local utility functions defined below
local HDG_ExportReagents
local HDG_ShowExportWindow

-- Slash command
SLASH_HDG1 = "/hdg"
SlashCmdList["HDG"] = function(msg)
    local cmd = msg and msg:lower():trim() or ""

    -- Debug commands (only available when debug mode is enabled)
    local isDebugCmd = cmd == "collection" or cmd == "tags" or cmd == "debuglumber" or cmd == "debugprof" or cmd == "tsm"
    if isDebugCmd and not GetConfig("debug") then
        HDG_UI.Toggle()
        return
    end

    if cmd == "collection" then
        -- Debug: Test catalog searcher for owned decor
        print("|cFF14b8a6[Housing Decor Guide]|r Testing collection searcher...")

        if not C_HousingCatalog then
            print("|cFFFF0000C_HousingCatalog not available|r")
            return
        end

        if not C_HousingCatalog.CreateCatalogSearcher then
            print("|cFFFF0000CreateCatalogSearcher not available|r")
            return
        end

        local catalogSearcher = C_HousingCatalog.CreateCatalogSearcher()
        if not catalogSearcher then
            print("|cFFFF0000Failed to create catalog searcher|r")
            return
        end

        print("|cFF00FF00Searcher created successfully|r")

        -- Check what methods are available
        local methods = {"SetOwnedOnly", "SetCollected", "SetUncollected", "GetAllSearchItems", "RunSearch", "SetResultsUpdatedCallback"}
        for _, method in ipairs(methods) do
            if catalogSearcher[method] then
                print(string.format("  |cFF00FF00%s|r: available", method))
            else
                print(string.format("  |cFFFF0000%s|r: NOT available", method))
            end
        end

        -- Try to configure and get results
        if catalogSearcher.SetOwnedOnly then
            catalogSearcher:SetOwnedOnly(true)
            print("|cFF00FF00SetOwnedOnly(true) called|r")
        end

        if catalogSearcher.SetCollected then
            catalogSearcher:SetCollected(true)
            print("|cFF00FF00SetCollected(true) called|r")
        end

        -- Try GetAllSearchItems
        if catalogSearcher.GetAllSearchItems then
            local items = catalogSearcher:GetAllSearchItems()
            if items then
                print(string.format("|cFFfcd34dGetAllSearchItems returned %d items|r", #items))
                -- Print first 5 items
                for i = 1, math.min(5, #items) do
                    local item = items[i]
                    print(string.format("  Item %d: type=%s, recordID=%s", i,
                        tostring(item.entryType), tostring(item.recordID)))
                end
            else
                print("|cFFFF0000GetAllSearchItems returned nil|r")
            end
        end

        -- Try async with callback
        if catalogSearcher.SetResultsUpdatedCallback and catalogSearcher.RunSearch then
            print("|cFF00FF00Trying async search with callback...|r")
            catalogSearcher:SetResultsUpdatedCallback(function()
                local items = catalogSearcher:GetAllSearchItems()
                if items then
                    print(string.format("|cFF00FF00Async callback: %d items found|r", #items))
                    for i = 1, math.min(3, #items) do
                        local item = items[i]
                        local ok, info = pcall(C_HousingCatalog.GetCatalogEntryInfo, item)
                        if ok and info then
                            print(string.format("  %d: %s", i, tostring(info.name)))
                            -- Dump all fields in info
                            for k, v in pairs(info) do
                                print(string.format("    %s = %s", tostring(k), tostring(v)))
                            end
                            -- Also dump entryID fields
                            print("    entryID fields:")
                            for k, v in pairs(item) do
                                print(string.format("      %s = %s", tostring(k), tostring(v)))
                            end
                        end
                    end
                else
                    print("|cFFFF0000Async callback: nil items|r")
                end
                if catalogSearcher.Release then
                    catalogSearcher:Release()
                end
            end)
            catalogSearcher:RunSearch()
        end

        return

    elseif cmd == "tags" then
        -- Dump all unique tags with counts
        print("|cFF14b8a6[Housing Decor Guide]|r Scanning all decor items for tags...")
        local tagCounts = {}
        local totalItems = 0
        local itemsWithTags = 0

        if HDG_DecorDB and C_HousingCatalog and C_HousingCatalog.GetCatalogEntryInfoByItem then
            for spellID, recipe in pairs(HDG_DecorDB) do
                if recipe.itemID then
                    totalItems = totalItems + 1
                    local decorInfo = C_HousingCatalog.GetCatalogEntryInfoByItem(recipe.itemID, true)
                    if decorInfo and decorInfo.dataTagsByID then
                        local hasTag = false
                        for _, tagName in pairs(decorInfo.dataTagsByID) do
                            tagCounts[tagName] = (tagCounts[tagName] or 0) + 1
                            hasTag = true
                        end
                        if hasTag then
                            itemsWithTags = itemsWithTags + 1
                        end
                    end
                end
            end
        end

        -- Convert to sorted array
        local tags = {}
        for tag, count in pairs(tagCounts) do
            table.insert(tags, { name = tag, count = count })
        end
        table.sort(tags, function(a, b) return a.count > b.count end)

        -- Print results
        print(string.format("|cFFfcd34dFound %d unique tags across %d/%d items:|r", #tags, itemsWithTags, totalItems))
        for i, tagData in ipairs(tags) do
            print(string.format("  |cFF14b8a6%s|r: %d items", tagData.name, tagData.count))
        end

    elseif cmd == "decor" then
        -- Filter profession window to show only House Decor recipes using text search
        if not C_TradeSkillUI then
            print("|cFFFF0000[Housing Decor Guide]|r Trade skill API not available.")
            return
        end

        if not C_TradeSkillUI.IsTradeSkillReady() then
            print("|cFFfcd34d[Housing Decor Guide]|r Open a profession window first, then run |cFFFFFFFF/hdg decor|r")
            return
        end

        -- Use text filter to search for "House Decor"
        if C_TradeSkillUI.SetRecipeItemNameFilter then
            C_TradeSkillUI.SetRecipeItemNameFilter("House Decor")
            print("|cFF14b8a6[Housing Decor Guide]|r Filtered to 'House Decor' recipes. Clear the search box to reset.")
        else
            print("|cFFFF0000[Housing Decor Guide]|r SetRecipeItemNameFilter not available in this WoW version.")
        end

    elseif cmd == "debuglumber" then
        -- Debug: dump lumber tracking state
        print("|cFF14b8a6[HDG]|r Lumber Tracking Debug:")

        -- Check settings
        print(string.format("  lumberCollapsed: %s", tostring(GetConfig("lumberCollapsed"))))
        print(string.format("  lumberPinned: %s", tostring(GetUIState("lumberPinned"))))

        -- Check LUMBER_TYPES
        local lumberTypes = HDG_ExpansionData.LUMBER_TYPES
        local lumberOrder = HDG_ExpansionData.LUMBER_ORDER
        local typeCount = 0
        for _ in pairs(lumberTypes or {}) do typeCount = typeCount + 1 end
        print(string.format("  LUMBER_TYPES entries: %d", typeCount))
        print(string.format("  LUMBER_ORDER entries: %d", lumberOrder and #lumberOrder or 0))

        -- Check session state
        if HDG_LumberTracker then
            local activeLumber = HDG_LumberTracker.GetActiveFarmingLumber and HDG_LumberTracker.GetActiveFarmingLumber()
            print(string.format("  Active farming lumber ID: %s", tostring(activeLumber)))

            if activeLumber and lumberTypes[activeLumber] then
                print(string.format("  Active lumber name: %s", lumberTypes[activeLumber].shortName or lumberTypes[activeLumber].name))
            end

            -- Check pinned widget state
            local isPinnedVisible = HDG_LumberTracker.IsPinnedVisible and HDG_LumberTracker.IsPinnedVisible()
            print(string.format("  Pinned widget visible: %s", tostring(isPinnedVisible)))

            -- Show previous counts vs current counts
            local prevCounts = HDG_LumberTracker.GetPreviousCounts and HDG_LumberTracker.GetPreviousCounts()
            local sessionStats = HDG_LumberTracker.GetSessionStats and HDG_LumberTracker.GetSessionStats()

            print("  |cFFfcd34dLumber Tracking State:|r")
            for _, lumberId in ipairs(lumberOrder or {}) do
                local info = lumberTypes[lumberId]
                local currentCount = HDG_Data.GetItemCount(lumberId) or 0
                local prevCount = prevCounts and prevCounts[lumberId] or "?"
                local stats = sessionStats and sessionStats[lumberId]
                local name = info and info.shortName or tostring(lumberId)

                local statStr = ""
                if stats then
                    statStr = string.format(" |cFF88FF88[Session: +%d, %.1f/hr]|r", stats.collected or 0, stats.perHour or 0)
                end

                print(string.format("    %s: now=%d, prev=%s%s", name, currentCount, tostring(prevCount), statStr))
            end
        else
            print("  |cFFFF0000HDG_LumberTracker not found!|r")
        end

        print("  |cFF888888Pick up lumber to test session tracking|r")

    elseif cmd == "debugprof" then
        -- Debug: dump profession skill data to a scrollable window
        local debugFrame = CreateFrame("Frame", "HDG_DebugFrame", UIParent, "BackdropTemplate")
        debugFrame:SetSize(700, 500)
        debugFrame:SetPoint("CENTER")
        debugFrame:SetBackdrop({
            bgFile = "Interface\\Buttons\\WHITE8x8",
            edgeFile = "Interface\\Buttons\\WHITE8x8",
            edgeSize = 1,
        })
        debugFrame:SetBackdropColor(0.1, 0.1, 0.1, 0.95)
        debugFrame:SetBackdropBorderColor(0.3, 0.3, 0.3, 1)
        debugFrame:SetMovable(true)
        debugFrame:EnableMouse(true)
        debugFrame:RegisterForDrag("LeftButton")
        debugFrame:SetScript("OnDragStart", debugFrame.StartMoving)
        debugFrame:SetScript("OnDragStop", debugFrame.StopMovingOrSizing)
        debugFrame:SetFrameStrata("DIALOG")

        -- Title
        local title = debugFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
        title:SetPoint("TOPLEFT", 12, -10)
        title:SetText("|cFF14b8a6HDG Debug: Profession Data|r")

        -- Close button
        local closeBtn = CreateFrame("Button", nil, debugFrame, "UIPanelCloseButton")
        closeBtn:SetPoint("TOPRIGHT", -2, -2)
        closeBtn:SetScript("OnClick", function() debugFrame:Hide() end)

        -- Scrollable edit box
        local scrollFrame = CreateFrame("ScrollFrame", nil, debugFrame, "UIPanelScrollFrameTemplate")
        scrollFrame:SetPoint("TOPLEFT", 10, -40)
        scrollFrame:SetPoint("BOTTOMRIGHT", -30, 10)

        local editBox = CreateFrame("EditBox", nil, scrollFrame)
        editBox:SetMultiLine(true)
        editBox:SetFontObject(GameFontHighlightSmall)
        editBox:SetWidth(640)
        editBox:SetAutoFocus(false)
        editBox:EnableMouse(true)
        editBox:SetScript("OnEscapePressed", function() debugFrame:Hide() end)
        scrollFrame:SetScrollChild(editBox)

        -- Build debug output
        local lines = {}
        table.insert(lines, "=== HDG Debug: Profession Data ===\n")

        -- Part 1: What does the WoW API return right now?
        table.insert(lines, "--- API Data (C_TradeSkillUI.GetChildProfessionInfos) ---")
        if C_TradeSkillUI and C_TradeSkillUI.GetChildProfessionInfos then
            local childInfos = C_TradeSkillUI.GetChildProfessionInfos()
            if childInfos and #childInfos > 0 then
                table.insert(lines, "Returned " .. #childInfos .. " entries:\n")
                for i, info in ipairs(childInfos) do
                    local expName = info.expansionName or "nil"
                    local profName = info.professionName or "nil"
                    local skill = info.skillLevel or 0
                    local maxSkill = info.maxSkillLevel or 0
                    table.insert(lines, string.format("  [%d] expansionName='%s'  skill=%d/%d", i, expName, skill, maxSkill))
                    table.insert(lines, string.format("       professionName='%s'", profName))

                    -- Show what normalization produces
                    if info.expansionName and HDG_ExpansionData then
                        local normalized = HDG_ExpansionData.NormalizeApiName(info.expansionName)
                        table.insert(lines, string.format("       -> Normalized to: '%s'", normalized))
                    end
                    table.insert(lines, "")
                end
            else
                table.insert(lines, "  ** No data returned - is a profession window open? **\n")
            end
        else
            table.insert(lines, "  ** GetChildProfessionInfos not available **\n")
        end

        -- Part 2: What's in SavedVariables?
        table.insert(lines, "\n--- SavedVariables (Store.characters) ---")
        local charKey = HDG_Data.GetCharacterKey()
        table.insert(lines, "Current character: " .. (charKey or "nil") .. "\n")

        local characters = GetCharacters()
        if characters and next(characters) then
            for cKey, charData in pairs(characters) do
                table.insert(lines, "Character: " .. cKey)
                if charData.professions then
                    for profName, profData in pairs(charData.professions) do
                        table.insert(lines, string.format("  Profession: %s (known=%d/%d)",
                            profName, profData.knownCount or 0, profData.totalCount or 0))
                        if profData.skillLevels then
                            local count = 0
                            for expName, skillData in pairs(profData.skillLevels) do
                                count = count + 1
                                table.insert(lines, string.format("    [%s] = %d/%d",
                                    expName, skillData.current or 0, skillData.max or 0))
                            end
                            if count == 0 then
                                table.insert(lines, "    (no skill levels stored)")
                            end
                        else
                            table.insert(lines, "    skillLevels = nil")
                        end
                    end
                else
                    table.insert(lines, "  (no professions)")
                end
                table.insert(lines, "")
            end
        else
            table.insert(lines, "  ** No character data in SavedVariables **\n")
        end

        -- Part 3: What does EXPANSION_ORDER contain?
        table.insert(lines, "\n--- Expansion Order (used by Alts tab) ---")
        local expansionOrder = HDG_Data.EXPANSION_ORDER or {}
        for i, expName in ipairs(expansionOrder) do
            table.insert(lines, string.format("  [%d] '%s'", i, expName))
        end

        -- Set text
        editBox:SetText(table.concat(lines, "\n"))
        print("|cFF14b8a6[HDG]|r Debug window opened. Press ESC or X to close.")

    elseif cmd == "exportreagents" then
        -- Export reagent data from current profession window to copyable popup
        HDG_ExportReagents()

    elseif cmd == "tsm" then
        -- Debug: Test TSM integration
        print("|cFF14b8a6[Housing Decor Guide]|r Testing TSM integration...")

        -- Check if TSM_API exists
        if not TSM_API then
            print("|cFFFF0000TSM_API global not found|r - TSM may not be installed or loaded")
            print("  Checking for Auctionator instead...")
            if Auctionator and Auctionator.API then
                print("|cFF00FF00Auctionator detected|r")
            else
                print("|cFFFF0000No price addon detected|r")
            end
            return
        end

        print("|cFF00FF00TSM_API found!|r")

        -- Check available methods
        local methods = {"GetCustomPriceValue", "ToItemString", "GetPriceSourceDescription"}
        for _, method in ipairs(methods) do
            if TSM_API[method] then
                print(string.format("  |cFF00FF00%s|r: available", method))
            else
                print(string.format("  |cFFFF0000%s|r: NOT available", method))
            end
        end

        -- Test price lookup with a decor item (Valdrakken Hanging Cauldron = 256430)
        local testItemID = 256430
        local testItemName = C_Item.GetItemNameByID(testItemID) or "Valdrakken Hanging Cauldron"
        print(string.format("\nTesting price lookup for %s (ID: %d):", testItemName, testItemID))

        -- Test different price sources (include sale rate variants)
        local priceSources = {"DBMarket", "DBMinBuyout", "DBHistorical", "DBRegionMarketAvg",
                              "DBRegionSaleRate", "dbregionsalerate", "DBRegionSoldPerDay", "dbregionsoldperday",
                              "DBRegionSaleAvg", "dbregionsaleavg"}
        local saleRateSources = {DBRegionSaleRate=true, dbregionsalerate=true, DBRegionSoldPerDay=true, dbregionsoldperday=true}
        for _, source in ipairs(priceSources) do
            local itemString = "i:" .. testItemID
            local price = TSM_API.GetCustomPriceValue(source, itemString)
            if price and price > 0 then
                if saleRateSources[source] then
                    -- Sale rate is a decimal (sales per day), not copper
                    print(string.format("  |cFF00FF00%s|r: %.4f/day", source, price))
                else
                    local gold = math.floor(price / 10000)
                    local silver = math.floor((price % 10000) / 100)
                    print(string.format("  |cFF00FF00%s|r: %d.%02dg", source, gold, silver))
                end
            else
                print(string.format("  |cFFFF0000%s|r: no data (raw: %s)", source, tostring(price)))
            end
        end

        -- Test the addon's GetItemPrice function
        print("\nTesting HDG_PriceIntegration.GetItemPrice:")
        local price, source = HDG_PriceIntegration.GetItemPrice(testItemID)
        if price then
            print(string.format("  Price: %s (source: %s)", HDG_PriceIntegration.FormatGold(price), source or "unknown"))
        else
            print("  |cFFFF0000No price returned|r")
        end

        -- Test TSM GetAuctionQuantity (for #AH column)
        print("\nTesting TSM_API.GetAuctionQuantity:")
        if TSM_API.GetAuctionQuantity then
            local itemString = "i:" .. testItemID
            local ahQty = TSM_API.GetAuctionQuantity(itemString)
            if ahQty then
                print(string.format("  AH Quantity: %d", ahQty))
            else
                print("  |cFFFF0000No AH quantity returned|r")
            end
        else
            print("  |cFFFF0000GetAuctionQuantity not available|r")
        end

        -- List all available price sources
        print("\nAvailable TSM Price Sources:")
        if TSM_API.GetPriceSourceKeys then
            local sources = {}
            TSM_API.GetPriceSourceKeys(sources)
            for _, key in ipairs(sources) do
                print(string.format("  %s", key))
            end
            print(string.format("  Total: %d sources", #sources))
        else
            print("  |cFFFF0000GetPriceSourceKeys not available|r")
        end

        -- Test ALL price sources for the decor item to find which ones have data
        print("\nTesting ALL price sources for decor item:")
        if TSM_API.GetPriceSourceKeys then
            local sources = {}
            TSM_API.GetPriceSourceKeys(sources)
            local itemString = "i:" .. testItemID
            for _, key in ipairs(sources) do
                local value = TSM_API.GetCustomPriceValue(key, itemString)
                if value and value > 0 then
                    print(string.format("  |cFF00FF00%s|r: %s", key, tostring(value)))
                end
            end
        end

    elseif cmd == "testquote" or cmd == "testquote shaman" or cmd == "testquote goblin" then
        -- Test quote display
        local HDG_CraftQuotes = privateDB.HDG_CraftQuotes
        local HDG_Vamoose = privateDB.HDG_Vamoose

        if not HDG_CraftQuotes or not HDG_Vamoose then
            print("|cFFFF0000[HDG]|r Quote system not loaded")
            return
        end

        -- Check for lumber speaker test
        local arg = msg:match("testquote%s+(%w+)")
        if arg == "shaman" or arg == "goblin" then
            local speakerType = arg:upper()
            local speaker = HDG_CraftQuotes.GetSpeaker(speakerType)
            if speaker then
                local testQuote = "This is a test quote for the " .. arg .. " speaker. DisplayID: " .. speaker.displayID
                print(string.format("|cFF14b8a6[HDG]|r Testing %s (displayID: %d)", speaker.name, speaker.displayID))
                HDG_Vamoose.PlayTalkingHead(testQuote, speakerType, speaker.displayID)
            else
                print(string.format("|cFFFF0000[HDG]|r No speaker found for: %s", arg))
            end
            return
        end

        -- List of professions and expansions for random selection
        local professions = {"Alchemy", "Blacksmithing", "Cooking", "Enchanting", "Engineering", "Inscription", "Jewelcrafting", "Leatherworking", "Tailoring"}
        local expansions = {"Classic", "TBC", "WotLK", "Cata", "MoP", "WoD", "Legion", "BfA", "SL", "DF", "TWW", "Midnight"}

        -- Pick random profession and expansion
        local profession = professions[math.random(1, #professions)]
        local expansion = expansions[math.random(1, #expansions)]

        -- Get a quote
        local quote = HDG_CraftQuotes.GetRandomQuote(profession, expansion)
        if quote then
            local speaker = HDG_CraftQuotes.GetSpeaker(profession)
            if speaker then
                print(string.format("|cFF14b8a6[HDG]|r Testing quote: %s (%s) - displayID: %d", profession, expansion, speaker.displayID))
                HDG_Vamoose.PlayTalkingHead(quote, profession, speaker.displayID)
            else
                print(string.format("|cFFFF0000[HDG]|r No speaker found for profession: %s", profession))
            end
        else
            print(string.format("|cFFFF0000[HDG]|r No quote found for %s / %s", profession, expansion))
        end

    elseif cmd == "dyes" or cmd == "dye" then
        if VamoosesDyeStudio and VamoosesDyeStudio.Toggle then
            VamoosesDyeStudio.Toggle()
        else
            print("|cFF00CED1Vamoose's Dye Studio|r is a standalone addon. Get it on CurseForge or Wago!")
        end

    elseif cmd == "hud" then
        -- Toggle lumber compass HUD
        if privateDB.HDG_LumberHUD then
            privateDB.HDG_LumberHUD.Toggle()
        else
            print("|cFFFF0000[Housing Decor Guide]|r Lumber HUD not available")
        end

    elseif cmd == "reset" then
        -- Emergency window reset: clear saved position and force to center
        local store = GetStore()
        if store then
            local state = store:GetState()
            if state and state.layout then state.layout.MainFrame = nil end
            if state and state.ui then state.ui.framePosition = nil end
        end
        local f = _G["HDG_MainFrame"]
        if f then
            f:ClearAllPoints()
            f:SetPoint("CENTER")
            f:Show()
            HDG_UI.RefreshUI()
            print("|cFF14b8a6[Housing Decor Guide]|r Window reset to center.")
        else
            -- Frame doesn't exist yet, create it fresh
            HDG_UI.CreateUI()
            f = _G["HDG_MainFrame"]
            if f then
                f:ClearAllPoints()
                f:SetPoint("CENTER")
                f:Show()
                HDG_UI.RefreshUI()
                print("|cFF14b8a6[Housing Decor Guide]|r Window created and reset to center.")
            else
                print("|cFFFF0000[Housing Decor Guide]|r Window reset failed. Try /reload then /hdg reset")
            end
        end

    else
        HDG_UI.Toggle()
    end
end

-- ============================================================================
-- EXPORT REAGENTS UTILITY
-- Extracts reagent item IDs from open profession window for database validation
-- ============================================================================

-- Export reagent data from current profession window (only House Decor recipes)
HDG_ExportReagents = function()
    if not C_TradeSkillUI.IsTradeSkillReady() then
        print("|cFFFF0000[HDG]|r Open a profession window first!")
        return
    end

    local recipeIDs = C_TradeSkillUI.GetAllRecipeIDs()
    if not recipeIDs or #recipeIDs == 0 then
        print("|cFFFF0000[HDG]|r No recipes found in profession window")
        return
    end

    local lines = {"RecipeSpellID,RecipeName,ReagentItemID,ReagentName,Qty"}
    local decorCount = 0

    for _, recipeID in ipairs(recipeIDs) do
        local recipeInfo = C_TradeSkillUI.GetRecipeInfo(recipeID)
        local isHouseDecor = false

        -- Check if recipe belongs to "House Decor" category
        if recipeInfo and recipeInfo.categoryID then
            local categoryInfo = C_TradeSkillUI.GetCategoryInfo(recipeInfo.categoryID)
            if categoryInfo and categoryInfo.name == "House Decor" then
                isHouseDecor = true
            end
        end

        if isHouseDecor then
            decorCount = decorCount + 1
            local schematic = C_TradeSkillUI.GetRecipeSchematic(recipeID, false)
            if schematic and schematic.reagentSlotSchematics then
                for _, slot in ipairs(schematic.reagentSlotSchematics) do
                    if slot.reagents then
                        for _, reagent in ipairs(slot.reagents) do
                            if reagent.itemID then
                                local itemName = C_Item.GetItemNameByID(reagent.itemID) or "Unknown"
                                table.insert(lines, string.format("%d,%s,%d,%s,%d",
                                    recipeID,
                                    schematic.name or "Unknown",
                                    reagent.itemID,
                                    itemName,
                                    slot.quantityRequired or 1
                                ))
                            end
                        end
                    end
                end
            end
        end
    end

    if decorCount == 0 then
        print("|cFFfcd34d[HDG]|r No House Decor recipes found in this profession window")
        return
    end

    -- Show popup window
    HDG_ShowExportWindow(table.concat(lines, "\n"), decorCount)
end

-- Create/show export popup window
HDG_ShowExportWindow = function(text, recipeCount)
    if not HDG_ReagentExportFrame then
        local f = CreateFrame("Frame", "HDG_ReagentExportFrame", UIParent, "BackdropTemplate")
        f:SetSize(600, 450)
        f:SetPoint("CENTER")
        f:SetBackdrop({
            bgFile = "Interface\\Buttons\\WHITE8X8",
            edgeFile = "Interface\\Buttons\\WHITE8X8",
            edgeSize = 1,
        })
        f:SetBackdropColor(0.1, 0.1, 0.12, 0.95)
        f:SetBackdropBorderColor(0.08, 0.72, 0.65, 1)
        f:SetMovable(true)
        f:EnableMouse(true)
        f:RegisterForDrag("LeftButton")
        f:SetScript("OnDragStart", f.StartMoving)
        f:SetScript("OnDragStop", f.StopMovingOrSizing)
        f:SetFrameStrata("DIALOG")

        local title = f:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
        title:SetPoint("TOP", 0, -12)
        title:SetText("|cFF14b8a6Export Reagent Data|r")

        local instr = f:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        instr:SetPoint("TOP", title, "BOTTOM", 0, -5)
        instr:SetText("|cFF888888Select all (Ctrl+A) then copy (Ctrl+C)|r")

        local sf = CreateFrame("ScrollFrame", nil, f, "UIPanelScrollFrameTemplate")
        sf:SetPoint("TOPLEFT", 15, -55)
        sf:SetPoint("BOTTOMRIGHT", -35, 45)

        local eb = CreateFrame("EditBox", nil, sf)
        eb:SetMultiLine(true)
        eb:SetFontObject("GameFontHighlightSmall")
        eb:SetWidth(540)
        eb:SetAutoFocus(false)
        eb:SetScript("OnEscapePressed", function(self) self:ClearFocus() end)
        sf:SetScrollChild(eb)
        f.editBox = eb

        local closeBtn = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
        closeBtn:SetPoint("BOTTOM", 0, 10)
        closeBtn:SetSize(80, 22)
        closeBtn:SetText("Close")
        closeBtn:SetScript("OnClick", function() f:Hide() end)

        HDG_ReagentExportFrame = f
    end

    HDG_ReagentExportFrame.editBox:SetText(text)
    HDG_ReagentExportFrame.editBox:HighlightText()
    HDG_ReagentExportFrame.editBox:SetFocus()
    HDG_ReagentExportFrame:Show()
    print("|cFF14b8a6[Housing Decor Guide]|r Exported reagents from " .. recipeCount .. " recipes.")
end

-- Throttle inventory updates
local inventoryUpdatePending = false
local function QueueInventoryUpdate()
    if inventoryUpdatePending then return end
    inventoryUpdatePending = true

    C_Timer.After(0.3, function()
        inventoryUpdatePending = false

        -- v2.1.4: Save current character's lumber inventory for account-wide tracking
        if HDG_Data and HDG_Data.SaveCharacterLumberInventory then
            HDG_Data.SaveCharacterLumberInventory()
        end

        -- v2.0.5: Check for lumber collection changes (updates farming stats)
        local lumberChanged = HDG_LumberTracker and HDG_LumberTracker.CheckForLumberChanges and HDG_LumberTracker.CheckForLumberChanges()

        local mainWindowVisible = privateDB.mainFrame and privateDB.mainFrame:IsShown()
        local pinnedLumberVisible = HDG_LumberTracker and HDG_LumberTracker.IsPinnedVisible and HDG_LumberTracker.IsPinnedVisible()

        -- Refresh main window if visible
        if mainWindowVisible then
            HDG_UI.RefreshCraftingQueue()
            HDG_UI.RefreshMaterials()
            HDG_UI.RefreshLumberTracking()
            Log(LOG_LEVEL.DEBUG, "Inventory updated - materials refreshed")
        -- Refresh pinned lumber widget even when main window is closed
        elseif pinnedLumberVisible then
            HDG_LumberTracker.RefreshPinned()
            Log(LOG_LEVEL.DEBUG, "Inventory updated - pinned lumber refreshed" .. (lumberChanged and " (lumber collected)" or ""))
        end

        -- Refresh standalone materials list if shown
        if HDG_MaterialsList and HDG_MaterialsList.IsShown and HDG_MaterialsList.IsShown() then
            HDG_MaterialsList.Refresh()
        end
    end)
end

-- Throttle recipe updates
local recipeUpdatePending = false
local function QueueRecipeUpdate()
    if recipeUpdatePending then return end
    recipeUpdatePending = true
    
    C_Timer.After(0.5, function()
        recipeUpdatePending = false
        if privateDB.mainFrame and privateDB.mainFrame:IsShown() then
            HDG_UI.RefreshUI()
            Log(LOG_LEVEL.DEBUG, "Recipe learned - UI refreshed")
        end
    end)
end

-- Event Handling
local eventFrame = CreateFrame("Frame")
eventFrame:RegisterEvent("ADDON_LOADED")
eventFrame:RegisterEvent("HOUSE_DECOR_ADDED_TO_CHEST")
eventFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
eventFrame:RegisterEvent("TRADE_SKILL_LIST_UPDATE")
eventFrame:RegisterEvent("TRADE_SKILL_SHOW")  -- v2.0.7: Auto-refresh alts tab when prof window opened
eventFrame:RegisterEvent("NEW_RECIPE_LEARNED")
eventFrame:RegisterEvent("BAG_UPDATE_DELAYED")
eventFrame:RegisterEvent("MAIL_INBOX_UPDATE")
eventFrame:RegisterEvent("MAIL_SHOW")  -- v2.0.6: Track mail open for lumber farming
eventFrame:RegisterEvent("MAIL_CLOSED")
eventFrame:RegisterEvent("MERCHANT_SHOW")  -- v2.1.4: Track merchant open for lumber farming
eventFrame:RegisterEvent("MERCHANT_CLOSED")
eventFrame:RegisterEvent("TRADE_SHOW")  -- v2.1.5: Track trade open for lumber farming
eventFrame:RegisterEvent("TRADE_CLOSED")
eventFrame:RegisterEvent("BANKFRAME_OPENED")  -- v2.0.6: Track bank open for lumber farming
eventFrame:RegisterEvent("BANKFRAME_CLOSED")
eventFrame:RegisterEvent("PLAYER_INTERACTION_MANAGER_FRAME_SHOW")  -- v2.2.x: Portable warband bank
eventFrame:RegisterEvent("PLAYER_INTERACTION_MANAGER_FRAME_HIDE")
eventFrame:RegisterEvent("PLAYER_MONEY")
eventFrame:RegisterEvent("TRADE_SKILL_ITEM_CRAFTED_RESULT")  -- v1.9.5: Crafting history tracking
eventFrame:RegisterEvent("PLAYER_LOGOUT")  -- v2.0.5: Save farming sessions on logout
eventFrame:RegisterEvent("ZONE_CHANGED_NEW_AREA")  -- v2.2.2: Re-init map context after zone transition
eventFrame:RegisterEvent("ACHIEVEMENT_EARNED")  -- Lumber mounted-harvesting achievement progress

eventFrame:SetScript("OnEvent", function(self, event, arg1, ...)
    if event == "ADDON_LOADED" and arg1 == addonName then
        -- Validate settings (legacy - still needed for first-time defaults)
        local success, err = pcall(privateDB.ValidateSettings)
        if not success then
            Log(LOG_LEVEL.ERROR, "Settings validation failed: %s", err)
        end

        -- Phase 2: Initialize Store and migrate settings
        if privateDB.InitSettingsProxy then
            local proxySuccess, proxyErr = pcall(privateDB.InitSettingsProxy)
            if not proxySuccess then
                Log(LOG_LEVEL.ERROR, "Store initialization failed: %s", proxyErr)
            end
        end

        -- Phase 3: Initialize Theme Engine
        local HDG = _G.HousingDecorGuide
        local themeSuccess, themeErr = pcall(function() HDG.Theme:Initialize() end)
        if not themeSuccess then
            Log(LOG_LEVEL.ERROR, "Theme Engine initialization failed: %s", themeErr)
        end

        -- Apply locale override if set (after SavedVariables loaded)
        if HDG_L and HDG_L.ApplyOverride then
            HDG_L:ApplyOverride()
        end

        -- Initialize minimap button after settings are loaded
        if privateDB.HDG_Minimap then
            privateDB.HDG_Minimap.Initialize()
        end

        -- Load persisted direct AH price cache
        HDG_PriceIntegration.LoadCache()

        -- Initialize waypoint map pins (deferred until WorldMapFrame loads)
        C_Timer.After(2, function() HDG.Waypoints:InitMapPins() end)

        -- v2.0: Collect character profession data for multi-character tracking
        C_Timer.After(1, function()
            HDG_Data.CollectCharacterProfessions()
            Log(LOG_LEVEL.DEBUG, "Character profession data collected")
        end)

        -- Tooltip: tag housing decor reagents
        TooltipDataProcessor.AddTooltipPostCall(Enum.TooltipDataType.Item, function(tooltip, data)
            if tooltip ~= GameTooltip then return end
            local store = HDG and HDG.Store
            local state = store and store:GetState()
            if not state or not state.config.tooltipDecorTag then return end
            local itemID = data and data.id
            if not itemID then return end
            if not HDG_PowerCrafting or not HDG_PowerCrafting.GetRecipesForReagent then return end
            local recipes = HDG_PowerCrafting.GetRecipesForReagent(itemID)
            if not recipes or #recipes == 0 then return end
            tooltip:AddLine(string.format("|cFF14b8a6Housing Decor reagent - %d recipe%s|r", #recipes, #recipes == 1 and "" or "s"))
        end)

        Log(LOG_LEVEL.INFO, "Addon loaded successfully")
        print(string.format("|cFF00FF00Vamoose's Housing Decor Helper v%s|r loaded! Type |cFFFFFF00/hdg|r to open.", privateDB.ADDON_VERSION))

    elseif event == "PLAYER_ENTERING_WORLD" then
        local isInitialLogin, isReloadingUi = arg1, ...
        -- v2.0.5: Initialize lumber session tracking (only on login/reload, NOT zone transitions)
        -- PLAYER_ENTERING_WORLD fires on zone changes too — re-init would wipe active sessions
        if (isInitialLogin or isReloadingUi) and HDG_LumberTracker and HDG_LumberTracker.InitializeSessionTracking then
            HDG_LumberTracker.InitializeSessionTracking()
        elseif HDG_LumberTracker and HDG_LumberTracker.SetUISuspended then
            -- Safety reset: clear uiSuspended on zone transitions
            -- Prevents stuck state if merchant/bank was open when player zoned
            HDG_LumberTracker.SetUISuspended(false)
        end

        -- v2.1.4: Save current character's lumber inventory on login
        -- Delay longer to ensure warband bank data is fully loaded
        if HDG_Data and HDG_Data.SaveCharacterLumberInventory then
            C_Timer.After(3.0, function()
                HDG_Data.SaveCharacterLumberInventory()
                -- v2.1.5: Refresh pinned lumber tracker after saving inventory
                -- Invalidate first to force re-render with fresh counts
                if HDG_LumberTracker and HDG_LumberTracker.IsPinnedVisible and HDG_LumberTracker.IsPinnedVisible() then
                    HDG_LumberTracker.InvalidateDisplayState()
                    HDG_LumberTracker.RefreshPinned()
                end
            end)
        end

        -- v2.0.2: Auto-show independent pinned lumber tracker on login
        if GetUIState("lumberPinned") then
            C_Timer.After(0.5, function()
                if HDG_LumberTracker and HDG_LumberTracker.RestorePinnedState then
                    HDG_LumberTracker.RestorePinnedState()
                end
            end)
        end

        -- Auto-restore pinned floating windows on login/reload (not zone transitions)
        if isInitialLogin or isReloadingUi then
            if GetUIState("vendorListPinned") then
                C_Timer.After(0.8, function()
                    if HDG_VendorShoppingList and HDG_VendorShoppingList.Show then
                        HDG_VendorShoppingList.Show()
                    end
                end)
            end
            if GetUIState("materialsListPinned") then
                C_Timer.After(0.8, function()
                    if HDG_MaterialsList and HDG_MaterialsList.Show then
                        HDG_MaterialsList.Show()
                    end
                end)
            end
        end

        -- Zone scanner on login/reload (ZONE_CHANGED_NEW_AREA doesn't fire on /reload)
        -- 3s delay: housing catalog API needs time to populate collection data after login
        if (isInitialLogin or isReloadingUi) and HDG_ZoneMode and HDG_ZoneMode.OnZoneChanged then
            C_Timer.After(3.0, function()
                local mapID = C_Map.GetBestMapForUnit("player")
                if mapID then HDG_ZoneMode.OnZoneChanged(mapID) end
            end)
        end

    elseif event == "ZONE_CHANGED_NEW_AREA" then
        -- v2.2.2: Re-initialize map context after zone transition
        -- The map API can return nil briefly during zone transitions, which breaks lumber tracking
        -- Debounce rapid zone transitions
        if privateDB._zoneChangeTimer then privateDB._zoneChangeTimer:Cancel() end
        privateDB._zoneChangeTimer = C_Timer.NewTimer(0.5, function()
            privateDB._zoneChangeTimer = nil
            local mapID = C_Map.GetBestMapForUnit("player")
            if mapID then
                -- Refresh pinned lumber tracker to use new zone's map context
                if HDG_LumberTracker and HDG_LumberTracker.IsPinnedVisible and HDG_LumberTracker.IsPinnedVisible() then
                    HDG_LumberTracker.InvalidateDisplayState()
                    HDG_LumberTracker.RefreshPinned()
                end
                -- Notify lumber HUD of zone change so it can refresh its blip positions
                if privateDB.HDG_LumberHUD and privateDB.HDG_LumberHUD.OnZoneChanged then
                    privateDB.HDG_LumberHUD.OnZoneChanged(mapID)
                end
                -- Notify zone mode of zone change for zone scanner
                if HDG_ZoneMode and HDG_ZoneMode.OnZoneChanged then
                    HDG_ZoneMode.OnZoneChanged(mapID)
                end
                Log(LOG_LEVEL.DEBUG, "Zone changed - lumber tracker refreshed (mapID: %d)", mapID)
            end
        end)

    elseif event == "HOUSE_DECOR_ADDED_TO_CHEST" then
        local decorID = ...
        Log(LOG_LEVEL.DEBUG, "Decor added to collection (decorID: " .. tostring(decorID) .. ")")

        -- Log acquisition to history
        local HDG_DataTab = privateDB.HDG_DataTab
        if decorID and HDG_DataTab and HDG_DataTab.LogAcquisition then
            HDG_DataTab.LogAcquisition(decorID)
        end

        -- Immediately mark this decorID as collected in cache
        -- This bypasses Blizzard API delays that cause "one item behind" issues
        if decorID and HDG_Data.MarkDecorCollected then
            HDG_Data.MarkDecorCollected(decorID)
        end

        -- Refresh shopping list immediately (cache is already updated from MarkDecorCollected)
        if HDG_VendorShoppingList and HDG_VendorShoppingList.IsShown and HDG_VendorShoppingList.IsShown() then
            HDG_VendorShoppingList.Refresh()
        end

        -- Single deferred refresh to catch Blizzard API updates (cache already updated above)
        C_Timer.After(0.5, function()
            if privateDB.mainFrame and privateDB.mainFrame:IsShown() then
                HDG_UI.RefreshUI()
            end
        end)

    elseif event == "TRADE_SKILL_LIST_UPDATE" or event == "NEW_RECIPE_LEARNED" then
        HDG_Data.InvalidateRecipeCache()
        QueueRecipeUpdate()
        if HDG_LumberTracker and HDG_LumberTracker.InvalidateLumberNeeds then HDG_LumberTracker.InvalidateLumberNeeds() end
        -- v2.0: Update character profession data when recipes change
        HDG_Data.CollectCharacterProfessions()

    elseif event == "TRADE_SKILL_SHOW" then
        -- v2.0.7: Refresh profession data when profession window opened
        C_Timer.After(0.3, function()
            HDG_Data.CollectCharacterProfessions()
            HDG.EventBus:Trigger("HDG_PROFESSION_DATA_UPDATED")
        end)

    elseif event == "MAIL_SHOW" or event == "BANKFRAME_OPENED" or
           event == "MERCHANT_SHOW" or event == "TRADE_SHOW" then
        -- Suspend lumber tracking when item-moving UIs are open
        if HDG_LumberTracker and HDG_LumberTracker.SetUISuspended then
            HDG_LumberTracker.SetUISuspended(true)
        end
        if event == "BANKFRAME_OPENED" then QueueInventoryUpdate() end

    elseif event == "MAIL_CLOSED" or event == "BANKFRAME_CLOSED" or
           event == "MERCHANT_CLOSED" or event == "TRADE_CLOSED" then
        -- Resume lumber tracking when item-moving UIs close
        if HDG_LumberTracker and HDG_LumberTracker.SetUISuspended then
            HDG_LumberTracker.SetUISuspended(false)
        end
        QueueInventoryUpdate()

    elseif event == "PLAYER_INTERACTION_MANAGER_FRAME_SHOW" then
        -- Portable warband bank (type 68 = AccountBanker) or standard banker (type 8)
        local interactionType = arg1
        if interactionType == Enum.PlayerInteractionType.AccountBanker or
           interactionType == Enum.PlayerInteractionType.Banker then
            if HDG_LumberTracker and HDG_LumberTracker.SetUISuspended then
                HDG_LumberTracker.SetUISuspended(true)
            end
        end

    elseif event == "PLAYER_INTERACTION_MANAGER_FRAME_HIDE" then
        local interactionType = arg1
        if interactionType == Enum.PlayerInteractionType.AccountBanker or
           interactionType == Enum.PlayerInteractionType.Banker then
            if HDG_LumberTracker and HDG_LumberTracker.SetUISuspended then
                HDG_LumberTracker.SetUISuspended(false)
            end
            QueueInventoryUpdate()
        end

    elseif event == "BAG_UPDATE_DELAYED" or
           event == "MAIL_INBOX_UPDATE" or
           event == "PLAYER_MONEY" then
        QueueInventoryUpdate()

    elseif event == "TRADE_SKILL_ITEM_CRAFTED_RESULT" then
        -- TRADE_SKILL_ITEM_CRAFTED_RESULT: result table with hyperlink, quantity, isCrit
        local result = arg1
        if result and result.hyperlink then
            HDG_Data.LogCraftFromItemLink(result.hyperlink, result.quantity or 1)

            -- Trigger craft quote for decor items
            local itemID = tonumber(result.hyperlink:match("item:(%d+)"))
            -- Only show craft quotes if master toggle enabled AND chattiness check passes
            local quotesEnabled = GetConfig("quotesEnabled")
            if quotesEnabled == nil then quotesEnabled = true end
            local chattiness = GetConfig("vamooseChattiness") or 30
            local craftQuotesEnabled = GetConfig("craftQuotesEnabled")
            if craftQuotesEnabled == nil then craftQuotesEnabled = true end
            local decorInfo = itemID and privateDB.decorItem and privateDB.decorItem[itemID]

            if quotesEnabled and decorInfo and craftQuotesEnabled and ShouldShowCraftQuote(chattiness) then
                if decorInfo.profession and decorInfo.expansion then
                    local expansionAbbr = HDG_ExpansionData.GetAbbreviation(decorInfo.expansion)
                    local HDG_CraftQuotes = privateDB.HDG_CraftQuotes
                    local HDG_Vamoose = privateDB.HDG_Vamoose
                    if HDG_CraftQuotes and HDG_Vamoose then
                        local quote = HDG_CraftQuotes.GetRandomQuote(decorInfo.profession, expansionAbbr)
                        if quote then
                            local speaker = HDG_CraftQuotes.GetSpeaker(decorInfo.profession)
                            if speaker then
                                local onlyChat = GetConfig("quotesOnlyChat") or false
                                if not onlyChat then
                                    HDG_Vamoose.PlayTalkingHead(quote, decorInfo.profession, speaker.displayID)
                                end
                                if onlyChat then
                                    print(string.format("|cFF14b8a6[%s]|r %s crafted! %s", speaker.name, result.hyperlink, quote))
                                end
                            end
                        end
                    end
                end
            end

            -- v2.0.8: Auto-decrement crafting queue when decor item crafted
            local queue = GetCraftingQueue()
            if decorInfo and queue then
                -- Crafting queue stores item.id as the itemID (not DecorDB key)
                do
                    local craftedQty = result.quantity or 1
                    for _, item in ipairs(queue) do
                        if item.id == itemID then
                            local newQty = item.quantity - craftedQty
                            if newQty <= 0 then
                                Dispatch("REMOVE_FROM_CRAFTING_QUEUE", { itemID = itemID })
                                print(string.format("|cFF14b8a6[HDG]|r %s removed from crafting queue (crafting complete)", item.name or "Item"))
                            else
                                Dispatch("UPDATE_CRAFTING_QUEUE_ITEM_QTY", { itemID = itemID, quantity = newQty })
                                print(string.format("|cFF14b8a6[HDG]|r %s: %d remaining in crafting queue", item.name or "Item", newQty))
                            end

                            -- Track pending crafts for multi-craft scenarios
                            -- Decrement pending count if this matches our tracked craft
                            if privateDB.pendingCraftInfo and privateDB.pendingCraftInfo.itemID == itemID then
                                privateDB.pendingCraftInfo.remaining = privateDB.pendingCraftInfo.remaining - craftedQty
                            end

                            -- Only close profession window if craft was initiated from addon
                            -- (pendingCraftInfo.itemID is set when crafting from addon UI)
                            local wasAddonInitiated = privateDB.pendingCraftInfo and privateDB.pendingCraftInfo.itemID == itemID

                            if wasAddonInitiated then
                                -- Check if all pending crafts are complete
                                local allCraftsComplete = (privateDB.pendingCraftInfo.remaining <= 0)

                                if allCraftsComplete then
                                    -- Clear tracking state
                                    privateDB.pendingCraftInfo.itemID = nil
                                    privateDB.pendingCraftInfo.remaining = 0

                                    -- Close profession window after crafting (if setting enabled)
                                    local closeAfterCraft = GetConfig("closeProfWindowAfterCraft")
                                    if closeAfterCraft == nil then closeAfterCraft = true end
                                    if closeAfterCraft then
                                        C_Timer.After(0.5, function()
                                            if InCombatLockdown() then return end
                                            if ProfessionsFrame and ProfessionsFrame:IsShown() then
                                                HideUIPanel(ProfessionsFrame)
                                            elseif C_TradeSkillUI and C_TradeSkillUI.CloseTradeSkill then
                                                C_TradeSkillUI.CloseTradeSkill()
                                            end
                                        end)
                                    end
                                end
                            end

                            -- Refresh UI
                            if privateDB.mainFrame and privateDB.mainFrame:IsShown() then
                                HDG_UI.RefreshUI()
                            end
                            break
                        end
                    end
                end
            end
        end

    elseif event == "ACHIEVEMENT_EARNED" then
        if HDG_LumberTracker and HDG_LumberTracker.IsLumberAchievement
           and HDG_LumberTracker.IsLumberAchievement(arg1) then
            HDG_LumberTracker.InvalidateDisplayState()
            if HDG_LumberTracker.IsPinnedVisible and HDG_LumberTracker.IsPinnedVisible() then
                HDG_LumberTracker.RefreshPinned()
            end
        end
        -- Refresh achievements display on Data tab for any housing achievement
        if HDG_ExpansionData.IsHousingAchievement(arg1) then
            local HDG_DataTab = privateDB.HDG_DataTab
            if HDG_DataTab and HDG_DataTab.RefreshAchievementsDisplay then
                HDG_DataTab.RefreshAchievementsDisplay()
            end
        end

    elseif event == "PLAYER_LOGOUT" then
        -- v2.0.5: Save farming sessions to history on logout
        if HDG_LumberTracker and HDG_LumberTracker.SaveAllSessions then
            HDG_LumberTracker.SaveAllSessions()
        end

        -- Phase 2: Flush Store to SavedVariables on logout
        HDG.Store:Flush()
    end
end)

-- ============================================================================
-- ADDON COMPARTMENT FUNCTIONS (Referenced by TOC metadata)
-- These global functions are called by Blizzard's addon compartment system
-- ============================================================================

function HousingDecorGuide_OnAddonCompartmentClick(addonName, buttonName)
    HDG_UI.Toggle()
end

function HousingDecorGuide_OnAddonCompartmentEnter(addonName, menuItem)
    GameTooltip:SetOwner(menuItem, "ANCHOR_RIGHT")
    GameTooltip:AddLine("|cFFfcd34dHousing Decor Guide|r", 1, 1, 1)
    GameTooltip:AddLine("Track and craft housing decor items", 0.7, 0.7, 0.7)
    GameTooltip:AddLine(" ")
    GameTooltip:AddLine("|cFFFFFFFFClick:|r Toggle addon window", 0.7, 0.7, 0.7)
    GameTooltip:Show()
end

function HousingDecorGuide_OnAddonCompartmentLeave(addonName, menuItem)
    GameTooltip:Hide()
end
