local addonName, privateDB = ...

-- ============================================================================
-- DEBUG MODULE
-- Centralized debug utilities, slash commands, and beta testing tools
-- ============================================================================

local HDG_Debug = {}
privateDB.HDG_Debug = HDG_Debug

-- Theme helper (delegating to ThemeEngine)
local HDG = _G.HousingDecorGuide
local function GetScheme() return HDG.Theme.GetCurrentScheme() end

-- Debug state
local debugEnabled = false
local debugUnlocked = nil  -- Session override for debugModeEnabled setting (nil = use setting)
local eventLogging = false
local eventLog = {}
local MAX_EVENT_LOG = 100

-- Debug output window
local debugWindow = nil
local debugScrollFrame = nil
local debugEditBox = nil
local debugOutputLines = {}

-- ============================================================================
-- DEBUG OUTPUT WINDOW
-- ============================================================================

local function CreateDebugWindow()
    if debugWindow then return debugWindow end

    -- Create main frame
    debugWindow = CreateFrame("Frame", "HDGDebugWindow", UIParent, "BackdropTemplate")
    debugWindow:SetSize(700, 500)
    debugWindow:SetPoint("CENTER")
    debugWindow:SetMovable(true)
    debugWindow:SetResizable(true)
    debugWindow:SetClampedToScreen(true)
    debugWindow:EnableMouse(true)
    debugWindow:SetFrameStrata("DIALOG")

    -- Backdrop
    debugWindow:SetBackdrop({
        bgFile = "Interface/Tooltips/UI-Tooltip-Background",
        edgeFile = "Interface/Tooltips/UI-Tooltip-Border",
        tile = true,
        tileSize = 16,
        edgeSize = 16,
        insets = { left = 4, right = 4, top = 4, bottom = 4 }
    })
    local scheme = GetScheme()
    if scheme then
        debugWindow:SetBackdropColor(scheme.bg.r, scheme.bg.g, scheme.bg.b, 0.95)
        debugWindow:SetBackdropBorderColor(scheme.border.r, scheme.border.g, scheme.border.b, scheme.border.a)
    else
        debugWindow:SetBackdropColor(0.1, 0.1, 0.1, 0.95)
        debugWindow:SetBackdropBorderColor(0.3, 0.3, 0.3, 1)
    end

    -- Make draggable
    debugWindow:SetScript("OnMouseDown", function(self, button)
        if button == "LeftButton" then
            self:StartMoving()
        end
    end)
    debugWindow:SetScript("OnMouseUp", function(self)
        self:StopMovingOrSizing()
    end)

    -- Title bar
    local title = debugWindow:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    title:SetPoint("TOP", 0, -10)
    title:SetText("|cFF00FFFF[HDG Debug Output]|r")

    -- Close button
    local closeBtn = CreateFrame("Button", nil, debugWindow, "UIPanelCloseButton")
    closeBtn:SetPoint("TOPRIGHT", -5, -5)

    -- Clear button
    local clearBtn = CreateFrame("Button", nil, debugWindow, "UIPanelButtonTemplate")
    clearBtn:SetSize(60, 22)
    clearBtn:SetPoint("TOPRIGHT", closeBtn, "TOPLEFT", -10, -3)
    clearBtn:SetText("Clear")
    clearBtn:SetScript("OnClick", function()
        HDG_Debug.ClearWindow()
    end)

    -- Copy button
    local copyBtn = CreateFrame("Button", nil, debugWindow, "UIPanelButtonTemplate")
    copyBtn:SetSize(60, 22)
    copyBtn:SetPoint("RIGHT", clearBtn, "LEFT", -5, 0)
    copyBtn:SetText("Copy")
    copyBtn:SetScript("OnClick", function()
        if debugEditBox then
            debugEditBox:HighlightText()
            debugEditBox:SetFocus()
        end
    end)

    -- Scroll frame
    debugScrollFrame = CreateFrame("ScrollFrame", "HDGDebugScrollFrame", debugWindow, "UIPanelScrollFrameTemplate")
    debugScrollFrame:SetPoint("TOPLEFT", 10, -40)
    debugScrollFrame:SetPoint("BOTTOMRIGHT", -30, 10)

    -- Edit box for text (allows copy/paste)
    debugEditBox = CreateFrame("EditBox", "HDGDebugEditBox", debugScrollFrame)
    debugEditBox:SetMultiLine(true)
    debugEditBox:SetFontObject(GameFontHighlightSmall)
    debugEditBox:SetWidth(debugScrollFrame:GetWidth())
    debugEditBox:SetAutoFocus(false)
    debugEditBox:SetScript("OnEscapePressed", function(self) self:ClearFocus() end)

    debugScrollFrame:SetScrollChild(debugEditBox)

    -- Resize grip
    local resizeGrip = CreateFrame("Button", nil, debugWindow)
    resizeGrip:SetSize(16, 16)
    resizeGrip:SetPoint("BOTTOMRIGHT", -6, 6)
    resizeGrip:SetNormalTexture("Interface/ChatFrame/UI-ChatIM-SizeGrabber-Up")
    resizeGrip:SetHighlightTexture("Interface/ChatFrame/UI-ChatIM-SizeGrabber-Highlight")
    resizeGrip:SetPushedTexture("Interface/ChatFrame/UI-ChatIM-SizeGrabber-Down")
    resizeGrip:SetScript("OnMouseDown", function()
        debugWindow:StartSizing("BOTTOMRIGHT")
    end)
    resizeGrip:SetScript("OnMouseUp", function()
        debugWindow:StopMovingOrSizing()
        debugEditBox:SetWidth(debugScrollFrame:GetWidth())
    end)

    debugWindow:SetResizeBounds(400, 300, 1200, 800)

    -- Hide by default
    debugWindow:Hide()

    return debugWindow
end

-- Add a line to the debug window
function HDG_Debug.WindowPrint(...)
    local args = { ... }
    local line = ""
    for i, v in ipairs(args) do
        if i > 1 then line = line .. " " end
        line = line .. tostring(v)
    end
    -- Strip color codes for editbox (they don't render there)
    local plainLine = line:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", "")
    table.insert(debugOutputLines, plainLine)

    -- Update editbox
    if debugEditBox then
        debugEditBox:SetText(table.concat(debugOutputLines, "\n"))
    end
end

-- Clear the debug window
function HDG_Debug.ClearWindow()
    wipe(debugOutputLines)
    if debugEditBox then
        debugEditBox:SetText("")
    end
end

-- Show the debug window
function HDG_Debug.ShowWindow(title)
    CreateDebugWindow()
    if title then
        local titleText = debugWindow:GetRegions()
        if titleText and titleText.SetText then
            titleText:SetText("|cFF00FFFF[HDG Debug: " .. title .. "]|r")
        end
    end
    debugWindow:Show()
end

-- Hide the debug window
function HDG_Debug.HideWindow()
    if debugWindow then
        debugWindow:Hide()
    end
end

-- ============================================================================
-- CORE DEBUG UTILITIES
-- ============================================================================

-- Debug function to dump table contents
function HDG_Debug.DumpTable(tbl, indent, maxDepth)
    indent = indent or ""
    maxDepth = maxDepth or 3
    if #indent / 2 >= maxDepth then
        print(indent .. "...")
        return
    end

    if type(tbl) ~= "table" then
        print(indent .. tostring(tbl))
        return
    end

    for k, v in pairs(tbl) do
        local keyStr = tostring(k)
        if type(v) == "table" then
            print(indent .. "|cFFfcd34d" .. keyStr .. "|r = {")
            HDG_Debug.DumpTable(v, indent .. "  ", maxDepth)
            print(indent .. "}")
        else
            print(indent .. "|cFFfcd34d" .. keyStr .. "|r = |cFF00FF00" .. tostring(v) .. "|r")
        end
    end
end

-- Print a debug message (only if debug mode enabled)
function HDG_Debug.Print(...)
    if debugEnabled then
        print("|cFF00FFFF[HDG Debug]|r", ...)
    end
end

-- Print a debug message (always, for important debug output)
function HDG_Debug.PrintAlways(...)
    print("|cFF00FFFF[HDG Debug]|r", ...)
end

-- Check if debug mode is enabled
function HDG_Debug.IsEnabled()
    return debugEnabled
end

-- Set debug unlocked state (called from config UI)
function HDG_Debug.SetUnlocked(enabled)
    debugUnlocked = enabled
end

-- Check if debug commands are unlocked
function HDG_Debug.IsUnlocked()
    if debugUnlocked ~= nil then
        return debugUnlocked
    end
    local db = _G.HDG_DB
    return db and db.config and db.config.debugModeEnabled
end

-- ============================================================================
-- EVENT LOGGING
-- ============================================================================

function HDG_Debug.LogEvent(event, ...)
    if not eventLogging then return end

    local args = { ... }
    local argStr = ""
    for i, arg in ipairs(args) do
        argStr = argStr .. tostring(arg)
        if i < #args then argStr = argStr .. ", " end
    end

    local entry = {
        time = date("%H:%M:%S"),
        event = event,
        args = argStr
    }

    table.insert(eventLog, 1, entry)

    -- Trim log
    while #eventLog > MAX_EVENT_LOG do
        table.remove(eventLog)
    end

    -- Print to chat if verbose
    if debugEnabled then
        print(string.format("|cFF888888[%s]|r |cFFfcd34d%s|r %s", entry.time, event, argStr))
    end
end

function HDG_Debug.GetEventLog()
    return eventLog
end

function HDG_Debug.ClearEventLog()
    wipe(eventLog)
    HDG_Debug.PrintAlways("Event log cleared")
end

-- ============================================================================
-- SLASH COMMANDS
-- ============================================================================

-- Main debug command
SLASH_HDGDEBUG1 = "/hdgdebug"
SlashCmdList["HDGDEBUG"] = function(msg)
    -- Check if debug mode is enabled in settings (unless enabling it)
    local args = {}
    for word in msg:gmatch("%S+") do
        table.insert(args, word:lower())
    end
    local cmd = args[1] or "help"

    -- Allow "unlock" command to bypass the setting check
    if cmd ~= "unlock" then
        -- Check session override first, then fall back to saved setting
        local isUnlocked = debugUnlocked
        if isUnlocked == nil then
            local db = _G.HDG_DB
            isUnlocked = db and db.config and db.config.debugModeEnabled
        end
        if not isUnlocked then
            print("|cFF00FFFF[HDG]|r Debug mode is disabled. Enable it in HDG Config > Advanced, or type |cFFfcd34d/hdgdebug unlock|r")
            return
        end
    end

    if cmd == "unlock" then
        -- Toggle debug mode (session override + persist to settings)
        debugUnlocked = not debugUnlocked
        -- Also persist to Store config
        if HDG and HDG.Store then
            HDG.Store:SetConfig("debugModeEnabled", debugUnlocked)
        end
        if debugUnlocked then
            print("|cFF00FFFF[HDG Debug]|r Debug mode |cFF00FF00UNLOCKED|r - /hdgdebug commands now available")
        else
            print("|cFF00FFFF[HDG Debug]|r Debug mode |cFFFF0000LOCKED|r")
        end
        return
    elseif cmd == "on" or cmd == "enable" then
        debugEnabled = true
        HDG_Debug.PrintAlways("Debug mode |cFF00FF00ENABLED|r")
    elseif cmd == "off" or cmd == "disable" then
        debugEnabled = false
        HDG_Debug.PrintAlways("Debug mode |cFFFF0000DISABLED|r")
    elseif cmd == "events" then
        local subcmd = args[2] or "status"
        if subcmd == "on" then
            eventLogging = true
            HDG_Debug.PrintAlways("Event logging |cFF00FF00ENABLED|r")
        elseif subcmd == "off" then
            eventLogging = false
            HDG_Debug.PrintAlways("Event logging |cFFFF0000DISABLED|r")
        elseif subcmd == "show" then
            HDG_Debug.PrintAlways("Recent events (" .. #eventLog .. "):")
            for i = 1, math.min(10, #eventLog) do
                local e = eventLog[i]
                print(string.format("  |cFF888888[%s]|r |cFFfcd34d%s|r %s", e.time, e.event, e.args))
            end
        elseif subcmd == "clear" then
            HDG_Debug.ClearEventLog()
        else
            HDG_Debug.PrintAlways("Event logging: " .. (eventLogging and "|cFF00FF00ON|r" or "|cFFFF0000OFF|r"))
        end
    elseif cmd == "item" then
        local itemID = tonumber(args[2])
        if itemID then
            HDG_Debug.DumpItemInfo(itemID)
        else
            HDG_Debug.PrintAlways("Usage: /hdgdebug item <itemID>")
        end
    elseif cmd == "decor" then
        local decorID = tonumber(args[2])
        if decorID then
            HDG_Debug.DumpDecorInfo(decorID)
        else
            HDG_Debug.PrintAlways("Usage: /hdgdebug decor <decorID>")
        end
    elseif cmd == "cache" then
        HDG_Debug.DumpCacheStats()
    elseif cmd == "catalog" then
        HDG_Debug.TestCatalogSearch()
    elseif cmd == "search" then
        local searchTerm = table.concat(args, " ", 2)
        if searchTerm and searchTerm ~= "" then
            HDG_Debug.SearchCatalogByName(searchTerm)
        else
            HDG_Debug.PrintAlways("Usage: /hdgdebug search <name> (e.g., /hdgdebug search lorewalker)")
        end
    elseif cmd == "checkids" then
        local ids = {unpack(args, 2)}
        HDG_Debug.CheckDecorIDs(unpack(ids))
    elseif cmd == "lore" then
        -- Quick check for Lorewalker's Bookcase decorIDs
        print("|cFF00FFFF[HDG Debug]|r Checking Lorewalker decorIDs...")
        for _, id in ipairs({3839, 11873}) do
            if C_HousingCatalog and C_HousingCatalog.GetCatalogEntryInfoByRecordID then
                local ok, info = pcall(C_HousingCatalog.GetCatalogEntryInfoByRecordID, 1, id, true)
                if ok and info and info.name then
                    print(string.format("  decorID %d = %s", id, info.name))
                else
                    print(string.format("  decorID %d = NOT FOUND", id))
                end
            else
                print("  C_HousingCatalog API not available")
                break
            end
        end
    elseif cmd == "scan" then
        HDG_Debug.ScanMissing()
    elseif cmd == "export" then
        HDG_Debug.ExportCatalog()
    elseif cmd == "api" then
        HDG_Debug.DumpHousingAPI()
    elseif cmd == "settings" then
        HDG_Debug.DumpSettings()
    elseif cmd == "status" then
        HDG_Debug.PrintStatus()
    else
        -- Help
        HDG_Debug.PrintAlways("Housing Decor Guide - Debug Commands")
        print("  |cFFfcd34d/hdgdebug on|off|r - Enable/disable debug mode")
        print("  |cFFfcd34d/hdgdebug events on|off|show|clear|r - Event logging")
        print("  |cFFfcd34d/hdgdebug item <itemID>|r - Dump item/decor info")
        print("  |cFFfcd34d/hdgdebug decor <decorID>|r - Dump decor by recordID")
        print("  |cFFfcd34d/hdgdebug search <name>|r - Search catalog for decor by name")
        print("  |cFFfcd34d/hdgdebug cache|r - Show cache statistics")
        print("  |cFFfcd34d/hdgdebug catalog|r - Test catalog search API")
        print("  |cFFfcd34d/hdgdebug scan|r - Find items missing from AllDecorDB")
        print("  |cFFfcd34d/hdgdebug export|r - Export full catalog to SavedVariables")
        print("  |cFFfcd34d/hdgdebug api|r - List C_HousingCatalog functions")
        print("  |cFFfcd34d/hdgdebug settings|r - Dump current settings")
        print("  |cFFfcd34d/hdgdebug status|r - Show addon status")
    end
end

-- ============================================================================
-- DEBUG DUMP FUNCTIONS
-- ============================================================================

function HDG_Debug.DumpItemInfo(itemID)
    HDG_Debug.PrintAlways("Dumping info for itemID: " .. itemID)

    -- Basic item info
    local itemName, itemLink = C_Item.GetItemInfo(itemID)
    if itemName then
        print("  |cFFfcd34dItem:|r " .. (itemLink or itemName))
    else
        print("  |cFFFF6600Item info not cached yet|r")
    end

    -- Housing catalog info
    if C_HousingCatalog and C_HousingCatalog.GetCatalogEntryInfoByItem then
        local decorInfo = C_HousingCatalog.GetCatalogEntryInfoByItem(itemID, true)
        if decorInfo then
            print("  |cFF00FF00Decor Info:|r")
            HDG_Debug.DumpTable(decorInfo, "    ")

            -- Category info
            if decorInfo.categoryIDs and decorInfo.categoryIDs[1] then
                local catID = decorInfo.categoryIDs[1]
                if C_HousingCatalog.GetCatalogCategoryInfo then
                    local catInfo = C_HousingCatalog.GetCatalogCategoryInfo(catID)
                    if catInfo then
                        print("  |cFF00FF00Category:|r " .. (catInfo.name or "unknown"))
                    end
                end
            end
        else
            print("  |cFFFF6600Not a housing decor item|r")
        end
    else
        print("  |cFFFF0000C_HousingCatalog not available|r")
    end

    -- Check our DB
    if HDG_DecorDB then
        for _, recipe in pairs(HDG_DecorDB) do
            if recipe.itemID == itemID then
                print("  |cFF00FF00Found in HDG_DecorDB:|r")
                print("    |cFFfcd34dname:|r " .. (recipe.name or "nil"))
                print("    |cFFfcd34dprofession:|r " .. (recipe.profession or "nil"))
                print("    |cFFfcd34dexpansion:|r " .. (recipe.expansion or "nil"))
                break
            end
        end
    end
end

function HDG_Debug.DumpDecorInfo(decorID)
    HDG_Debug.PrintAlways("Dumping info for decorID (recordID): " .. decorID)

    if C_HousingCatalog and C_HousingCatalog.GetCatalogEntryInfoByRecordID then
        local decorInfo = C_HousingCatalog.GetCatalogEntryInfoByRecordID(1, decorID, true)
        if decorInfo then
            print("  |cFF00FF00Decor Info:|r")
            HDG_Debug.DumpTable(decorInfo, "    ")
        else
            print("  |cFFFF0000No decor found with recordID " .. decorID .. "|r")
        end
    else
        print("  |cFFFF0000C_HousingCatalog.GetCatalogEntryInfoByRecordID not available|r")
    end
end

-- Search housing catalog by name pattern and print matching decorIDs
-- Uses chunked processing to avoid freezing the client
function HDG_Debug.SearchCatalogByName(searchTerm)
    searchTerm = searchTerm:lower()
    HDG_Debug.PrintAlways("Searching catalog for: |cFFfcd34d" .. searchTerm .. "|r (this may take a moment...)")

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

    local matches = {}
    local currentID = 1
    local maxID = 15000
    local chunkSize = 500

    local function ProcessChunk()
        local endID = math.min(currentID + chunkSize - 1, maxID)
        for decorID = currentID, endID do
            local ok, info = pcall(C_HousingCatalog.GetCatalogEntryInfoByRecordID, 1, decorID, true)
            if ok and info and info.name then
                if info.name:lower():find(searchTerm, 1, true) then
                    table.insert(matches, { id = decorID, name = info.name, collected = ((info.quantity or 0) > 0 or (info.numPlaced or 0) > 0) })
                end
            end
        end
        currentID = endID + 1

        if currentID <= maxID then
            C_Timer.After(0.01, ProcessChunk)  -- Process next chunk after brief delay
        else
            -- Done - print results
            if #matches == 0 then
                print("  |cFFFF6600No matches found|r")
            else
                print("  |cFF00FF00Found " .. #matches .. " matches:|r")
                for _, m in ipairs(matches) do
                    local status = m.collected and "|cFF00FF00[Collected]|r" or "|cFF888888[Not collected]|r"
                    print(string.format("    decorID |cFFfcd34d%d|r = %s %s", m.id, m.name, status))
                end
            end
        end
    end

    ProcessChunk()
end

-- Quick lookup of specific decorIDs (for debugging data issues)
function HDG_Debug.CheckDecorIDs(...)
    local ids = {...}
    if #ids == 0 then
        HDG_Debug.PrintAlways("Usage: /hdgdebug checkids 3839 11873")
        return
    end

    HDG_Debug.PrintAlways("Checking decorIDs:")
    for _, decorID in ipairs(ids) do
        local id = tonumber(decorID)
        if id then
            local ok, info = pcall(C_HousingCatalog.GetCatalogEntryInfoByRecordID, 1, id, true)
            if ok and info and info.name then
                local status = ((info.quantity or 0) > 0 or (info.numPlaced or 0) > 0) and "|cFF00FF00[Collected]|r" or ""
                print(string.format("  |cFFfcd34d%d|r = %s %s", id, info.name, status))
            else
                print(string.format("  |cFFfcd34d%d|r = |cFFFF0000not found|r", id))
            end
        end
    end
end

-- Scan catalog for items missing from AllDecorDB, output in DB format for copy-paste
-- Usage: /hdgdebug scan
function HDG_Debug.ScanMissing()
    if not C_HousingCatalog or not C_HousingCatalog.GetCatalogEntryInfoByRecordID then
        HDG_Debug.PrintAlways("|cFFFF0000C_HousingCatalog API not available|r")
        return
    end

    HDG_Debug.PrintAlways("Scanning catalog for items not in AllDecorDB...")

    local missing = {}
    local currentID = 1
    local maxID = 20000
    local chunkSize = 500

    -- Strip WoW escape sequences from sourceText
    local function StripEscapes(text)
        if not text then return "" end
        text = text:gsub("|c%x%x%x%x%x%x%x%x", "")
        text = text:gsub("|r", "")
        text = text:gsub("|T[^|]*|t", "")
        text = text:gsub("|H[^|]*|h", "")
        text = text:gsub("|h", "")
        text = text:gsub("|n", "\n")
        return text:match("^%s*(.-)%s*$") or text
    end

    -- Parse sourceText into sourceType, sourceName, sourceDetail
    local function ClassifySource(rawSource)
        if not rawSource or rawSource == "" then return 0, "", "" end
        local text = StripEscapes(rawSource)
        if text == "" then return 0, "", "" end

        -- Simple keyword types
        if text == "Crafted" then return 6, "", "" end
        if text == "Learned" then return 7, "", "" end
        if text == "Gathering" then return 8, "", "" end

        -- Parse key-value lines
        local kv = {}
        local isPromotion = false
        for line in text:gmatch("[^\n]+") do
            line = line:match("^%s*(.-)%s*$") or line
            if line == "Promotion" then
                isPromotion = true
            else
                local key, val = line:match("^([A-Za-z ]+):%s*(.+)$")
                if key and not kv[key] then
                    kv[key] = val
                end
            end
        end

        -- Classify by priority
        if kv["Achievement"] then return 1, kv["Achievement"], kv["Category"] or "" end
        if kv["World Quest"] then return 3, kv["World Quest"], kv["Zone"] or "" end
        if kv["Quest"] then return 2, kv["Quest"], kv["Zone"] or "" end
        if kv["Drop"] then return 4, kv["Drop"], kv["Zone"] or "" end
        if kv["Vendor"] or kv["Vendors"] then return 5, kv["Vendor"] or kv["Vendors"] or "", kv["Zone"] or "" end
        if kv["Treasure"] then return 9, kv["Treasure"], "" end
        if kv["Profession"] then return 11, kv["Profession"], "" end
        if isPromotion then return 10, "Promotion", "" end
        return 0, "", ""
    end

    -- Escape a string for Lua output
    local function LuaEscape(s)
        return s:gsub("\\", "\\\\"):gsub('"', '\\"')
    end

    local function ProcessChunk()
        local endID = math.min(currentID + chunkSize - 1, maxID)
        for decorID = currentID, endID do
            local ok, info = pcall(C_HousingCatalog.GetCatalogEntryInfoByRecordID, 1, decorID, true)
            if ok and info and info.name and info.itemID then
                if not HDG_AllDecorDB or not HDG_AllDecorDB[info.itemID] then
                    local sourceType, sourceName, sourceDetail = ClassifySource(info.sourceText)
                    table.insert(missing, {
                        itemID = info.itemID,
                        decorID = decorID,
                        name = info.name,
                        sourceType = sourceType,
                        sourceName = sourceName,
                        sourceDetail = sourceDetail,
                        rawSource = info.sourceText,
                    })
                end
            end
        end
        currentID = endID + 1

        if currentID <= maxID then
            C_Timer.After(0.01, ProcessChunk)
        else
            -- Done - output to debug window
            HDG_Debug.ClearWindow()
            HDG_Debug.ShowWindow("Missing Items (" .. #missing .. ")")
            local W = HDG_Debug.WindowPrint

            if #missing == 0 then
                W("No missing items found - AllDecorDB is up to date!")
                HDG_Debug.PrintAlways("|cFF00FF00No missing items found.|r")
                return
            end

            -- Sort by itemID for clean output
            table.sort(missing, function(a, b) return a.itemID < b.itemID end)

            W("-- Missing items: " .. #missing .. " (paste into HDG_AllDecorDB.lua)")
            W("-- Format: [itemID] = {decorID, sourceType, \"sourceName\", \"sourceDetail\", 0}")
            W("")

            local timestamp = time()
            for _, item in ipairs(missing) do
                local raw = StripEscapes(item.rawSource or ""):gsub("\n", " | ")
                local comment = item.name
                if raw ~= "" then comment = comment .. " -- " .. raw end
                local line = string.format('    [%d] = {%d, %d, "%s", "%s", %d}, -- %s',
                    item.itemID, item.decorID, item.sourceType,
                    LuaEscape(item.sourceName), LuaEscape(item.sourceDetail), timestamp, comment)
                W(line)
            end

            HDG_Debug.PrintAlways(string.format("|cFF00FF00Scan complete: %d missing items.|r Click Copy in debug window.", #missing))
        end
    end

    ProcessChunk()
end

-- Export full catalog to SavedVariables for offline analysis
-- Usage: /hdgdebug export → /reload → read HDG_DB.catalogExport
function HDG_Debug.ExportCatalog()
    if not C_HousingCatalog or not C_HousingCatalog.GetCatalogEntryInfoByRecordID then
        HDG_Debug.PrintAlways("|cFFFF0000C_HousingCatalog API not available|r")
        return
    end

    HDG_Debug.PrintAlways("Exporting full catalog to SavedVariables...")

    local export = {}
    local count = 0
    local currentID = 1
    local maxID = 20000
    local chunkSize = 500

    local function ProcessChunk()
        local endID = math.min(currentID + chunkSize - 1, maxID)
        for decorID = currentID, endID do
            local ok, info = pcall(C_HousingCatalog.GetCatalogEntryInfoByRecordID, 1, decorID, true)
            if ok and info and info.name then
                local entry = {
                    name = info.name,
                    collected = ((info.quantity or 0) > 0 or (info.numPlaced or 0) > 0),
                }
                if info.itemID then entry.itemID = info.itemID end
                if info.sourceText and info.sourceText ~= "" then
                    entry.source = info.sourceText
                end
                if info.dataTagsByID then
                    local tags = {}
                    for _, t in pairs(info.dataTagsByID) do tags[#tags+1] = t end
                    if #tags > 0 then entry.tags = table.concat(tags, ",") end
                end
                export[decorID] = entry
                count = count + 1
            end
        end
        currentID = endID + 1

        if currentID <= maxID then
            C_Timer.After(0.01, ProcessChunk)
        else
            -- Done - save to HDG_DB
            if _G.HDG_DB then
                _G.HDG_DB.catalogExport = export
                HDG_Debug.PrintAlways(string.format("|cFF00FF00Exported %d catalog items to HDG_DB.catalogExport|r", count))
                HDG_Debug.PrintAlways("Type |cFFfcd34d/reload|r then check SavedVariables file")
            else
                HDG_Debug.PrintAlways("|cFFFF0000HDG_DB not available|r")
            end
        end
    end

    ProcessChunk()
end

function HDG_Debug.DumpCacheStats()
    HDG_Debug.PrintAlways("Cache Statistics:")

    local HDG_Data = privateDB.HDG_Data
    if HDG_Data then
        -- Try to access cache info if available
        print("  |cFFfcd34dHDG_Data module loaded|r")
    else
        print("  |cFFFF6600HDG_Data not available|r")
    end

    -- Shopping list
    if HDG_ShoppingList then
        local count = 0
        for _ in pairs(HDG_ShoppingList) do count = count + 1 end
        print("  |cFFfcd34dShopping List:|r " .. count .. " items")
    end

    -- Character data
    if HDG_CharacterData then
        local charCount = 0
        for _ in pairs(HDG_CharacterData) do charCount = charCount + 1 end
        print("  |cFFfcd34dCharacter Data:|r " .. charCount .. " characters")
    end
end

function HDG_Debug.TestCatalogSearch()
    -- Use debug window for output
    local W = HDG_Debug.WindowPrint
    HDG_Debug.ClearWindow()
    HDG_Debug.ShowWindow("Catalog Search API")

    W("Testing Catalog Search API...")
    W("")

    if not C_HousingCatalog then
        W("[ERROR] C_HousingCatalog not available")
        return
    end

    -- Check for CreateCatalogSearcher
    if not C_HousingCatalog.CreateCatalogSearcher then
        W("[ERROR] CreateCatalogSearcher not available")
        W("")
        W("Available functions:")
        for k, v in pairs(C_HousingCatalog) do
            W("  " .. k .. " - " .. type(v))
        end
        return
    end

    W("[OK] CreateCatalogSearcher exists")

    local catalogSearcher = C_HousingCatalog.CreateCatalogSearcher()
    if not catalogSearcher then
        W("[ERROR] CreateCatalogSearcher returned nil")
        return
    end

    W("[OK] Searcher created")
    W("Searcher type: " .. type(catalogSearcher))
    W("")

    -- Check for metatable (WoW objects often use metatables)
    local mt = getmetatable(catalogSearcher)
    if mt then
        W("[OK] Has metatable")
        if mt.__index and type(mt.__index) == "table" then
            W("Metatable methods:")
            for k, v in pairs(mt.__index) do
                W("  " .. tostring(k) .. " - " .. type(v))
            end
        end
    else
        W("[WARN] No metatable found")
    end
    W("")

    -- Direct method checks
    W("Direct method checks:")
    W("  SetOwnedOnly: " .. type(catalogSearcher.SetOwnedOnly))
    W("  SetCollected: " .. type(catalogSearcher.SetCollected))
    W("  SetUncollected: " .. type(catalogSearcher.SetUncollected))
    W("  SetResultsUpdatedCallback: " .. type(catalogSearcher.SetResultsUpdatedCallback))
    W("  RunSearch: " .. type(catalogSearcher.RunSearch))
    W("  GetNumResults: " .. type(catalogSearcher.GetNumResults))
    W("  GetResult: " .. type(catalogSearcher.GetResult))
    W("  GetCatalogSearchResults: " .. type(catalogSearcher.GetCatalogSearchResults))
    W("  GetAllSearchItems: " .. type(catalogSearcher.GetAllSearchItems))
    W("")

    -- Try to configure and run search
    W("Configuring search...")

    if catalogSearcher.SetOwnedOnly then
        catalogSearcher:SetOwnedOnly(false)
        W("  SetOwnedOnly(false) - OK")
    else
        W("  [WARN] SetOwnedOnly not available")
    end

    if catalogSearcher.SetCollected then
        catalogSearcher:SetCollected(true)
        W("  SetCollected(true) - OK")
    else
        W("  [WARN] SetCollected not available")
    end

    if catalogSearcher.SetUncollected then
        catalogSearcher:SetUncollected(true)
        W("  SetUncollected(true) - OK")
    else
        W("  [WARN] SetUncollected not available")
    end
    W("")

    -- Set callback
    catalogSearcher:SetResultsUpdatedCallback(function()
        W("[OK] Search callback fired!")
        W("")

        -- Try multiple methods to get results count
        local count = 0
        if catalogSearcher.GetNumResults then
            count = catalogSearcher:GetNumResults()
            W("GetNumResults: " .. count)
        else
            W("[WARN] GetNumResults not available")
        end

        -- Try GetCatalogSearchResults (what HDG_DecorPreviewTab uses)
        local allItems = nil
        if catalogSearcher.GetCatalogSearchResults then
            allItems = catalogSearcher:GetCatalogSearchResults()
            W("GetCatalogSearchResults: " .. (allItems and #allItems or "nil") .. " items")
        else
            W("[WARN] GetCatalogSearchResults not available")
        end

        -- Try GetAllSearchItems (fallback method)
        if catalogSearcher.GetAllSearchItems then
            local items2 = catalogSearcher:GetAllSearchItems()
            W("GetAllSearchItems: " .. (items2 and #items2 or "nil") .. " items")
            if not allItems or #allItems == 0 then
                allItems = items2
            end
        else
            W("[WARN] GetAllSearchItems not available")
        end

        -- Try GetResult (indexed access)
        if catalogSearcher.GetResult then
            W("[OK] GetResult method available")
            if count > 0 then
                W("First 3 results via GetResult:")
                for i = 1, math.min(3, count) do
                    local result = catalogSearcher:GetResult(i)
                    if result then
                        for k, v in pairs(result) do
                            W("    " .. tostring(k) .. " = " .. tostring(v))
                        end
                    end
                end
            end
        else
            W("[WARN] GetResult not available")
        end
        W("")

        -- Show first few results from allItems if we got any
        if allItems and #allItems > 0 then
            W("First 5 items from allItems:")
            W("")
            for i = 1, math.min(5, #allItems) do
                local entry = allItems[i]
                if entry then
                    W("--- Entry " .. i .. " (type: " .. type(entry) .. ") ---")
                    if type(entry) == "table" then
                        for k, v in pairs(entry) do
                            W("  " .. tostring(k) .. " = " .. tostring(v))
                        end
                        -- Try GetCatalogEntryInfo with table entry
                        if C_HousingCatalog.GetCatalogEntryInfo then
                            local ok, info = pcall(C_HousingCatalog.GetCatalogEntryInfo, entry)
                            if ok and info then
                                W("  [GetCatalogEntryInfo]:")
                                W("    name: " .. tostring(info.name))
                                W("    iconTexture: " .. tostring(info.iconTexture))
                                W("    recordID: " .. tostring(info.recordID))
                                if info.dataTagsByID then
                                    local tags = {}
                                    for _, tag in pairs(info.dataTagsByID) do
                                        table.insert(tags, tag)
                                    end
                                    W("    tags: " .. table.concat(tags, ", "))
                                end
                            else
                                W("  [ERROR] GetCatalogEntryInfo failed: " .. tostring(info))
                            end
                        end
                    elseif type(entry) == "userdata" then
                        W("  Entry is userdata - checking fields:")
                        W("    recordID: " .. tostring(entry.recordID))
                        W("    entryType: " .. tostring(entry.entryType))
                        -- Try GetCatalogEntryInfo
                        if C_HousingCatalog.GetCatalogEntryInfo then
                            local ok, info = pcall(C_HousingCatalog.GetCatalogEntryInfo, entry)
                            if ok and info then
                                W("  [GetCatalogEntryInfo]:")
                                for k, v in pairs(info) do
                                    W("    " .. tostring(k) .. " = " .. tostring(v))
                                end
                            else
                                W("  [ERROR] GetCatalogEntryInfo failed")
                            end
                        end
                    else
                        W("  Value: " .. tostring(entry))
                    end
                    W("")
                end
            end
        end

        W("=== Search Complete ===")

        -- Release
        if catalogSearcher.Release then
            catalogSearcher:Release()
        end
    end)

    -- Run search
    W("Running search...")
    if catalogSearcher.RunSearch then
        catalogSearcher:RunSearch()
        W("[OK] RunSearch called - waiting for callback...")
    else
        W("[ERROR] RunSearch not available")
    end
end

function HDG_Debug.DumpHousingAPI()
    local W = HDG_Debug.WindowPrint
    HDG_Debug.ClearWindow()
    HDG_Debug.ShowWindow("C_HousingCatalog API")

    W("C_HousingCatalog API Functions:")
    W("")

    if C_HousingCatalog then
        local funcs = {}
        for k, _ in pairs(C_HousingCatalog) do
            table.insert(funcs, k)
        end
        table.sort(funcs)

        for _, name in ipairs(funcs) do
            W("  " .. name .. " - " .. type(C_HousingCatalog[name]))
        end
        W("")
        W("Total: " .. #funcs .. " functions")
    else
        W("[ERROR] C_HousingCatalog not available")
    end
end

function HDG_Debug.DumpSettings()
    local W = HDG_Debug.WindowPrint
    HDG_Debug.ClearWindow()
    HDG_Debug.ShowWindow("Settings")

    W("Current HDG_Settings:")
    W("")

    if HDG_Settings then
        local function dumpToWindow(tbl, indent)
            indent = indent or ""
            for k, v in pairs(tbl) do
                if type(v) == "table" then
                    W(indent .. tostring(k) .. " = {")
                    dumpToWindow(v, indent .. "  ")
                    W(indent .. "}")
                else
                    W(indent .. tostring(k) .. " = " .. tostring(v))
                end
            end
        end
        dumpToWindow(HDG_Settings, "  ")
    else
        W("[WARN] HDG_Settings not loaded")
    end
end

function HDG_Debug.PrintStatus()
    local W = HDG_Debug.WindowPrint
    HDG_Debug.ClearWindow()
    HDG_Debug.ShowWindow("Addon Status")

    W("Housing Decor Guide - Status")
    W("")
    W("Debug Mode: " .. (debugEnabled and "ON" or "OFF"))
    W("Event Logging: " .. (eventLogging and "ON" or "OFF"))
    W("Events Logged: " .. #eventLog)
    W("")

    -- Module status
    W("Modules:")
    local modules = { "HDG_Data", "HDG_UI", "HDG_RecipesTab", "HDG_DecorPreviewTab", "HDG_LumberTracker" }
    for _, modName in ipairs(modules) do
        local loaded = privateDB[modName] ~= nil
        W("  " .. modName .. ": " .. (loaded and "loaded" or "not loaded"))
    end
    W("")

    -- API availability
    W("APIs:")
    W("  C_HousingCatalog: " .. (C_HousingCatalog and "available" or "missing"))
    W("  C_TradeSkillUI: " .. (C_TradeSkillUI and "available" or "missing"))
end

-- ============================================================================
-- EXPORTS
-- ============================================================================

-- Export to global namespace for easy access
HousingDecorGuide.Debug = HDG_Debug
HousingDecorGuide.DumpTable = HDG_Debug.DumpTable

print("|cFF00FF00[Housing Decor Guide]|r Debug module loaded. Use |cFFfcd34d/hdgdebug|r for commands.")
