-- ============================================================
-- Decor Vendor Data
-- Debug.lua
-- Debug commands, catalog checks, and copy/paste exports
-- ============================================================
--
-- Slash Commands:
--
-- /dvdata
--   Shows this command list.
--
-- /dvdata count
--   Prints total ActiveItems, unique decorIDs, and missing decorID count.
--
-- /dvdata catalog
--   Checks which DecorVendorData decorIDs are visible in the current Housing Catalog.
--
-- /dvdata groups
--   Prints item counts for every registered data group/file.
--
-- /dvdata group Midnight
--   Prints the item count for one specific group.
--   Example: /dvdata group The Burning Crusade
--
-- /dvdata hidden
--   Prints page 1 of decor items hidden/missing from the current Housing Catalog.
--
-- /dvdata hidden 2
--   Prints page 2 of hidden/missing catalog items.
--
-- /dvdata hidden groups
--   Prints hidden/missing item counts grouped by data file/category.
--
-- /dvdata target (or /dvdata check)
--   Scrapes current merchant items with live catalog pricing/zone cross-referencing.
--
-- /dvhidden
--   Opens a copy/paste popout box with all hidden/missing catalog items.
--
-- ============================================================

local addonName, DVD = ...

_G.DecorVendorData = DVD
_G[addonName] = DVD

DVD.ActiveItems = DVD.ActiveItems or {}
DVD.ActiveItemsByExpansion = DVD.ActiveItemsByExpansion or {}

-- Global tracking variables for multi-page stream caching
DVD.ScraperSessionDump = DVD.ScraperSessionDump or {}
DVD.ScraperSessionNPC = DVD.ScraperSessionNPC or 0

-- ============================================================
-- Basic Counts
-- ============================================================

function DVD.DebugCounts()
    local total = 0
    local noDecorID = 0
    local uniqueDecorIDs = 0
    local seenDecorIDs = {}

    for itemID, data in pairs(DVD.ActiveItems or {}) do
        total = total + 1

        if data.decorID then
            if not seenDecorIDs[data.decorID] then
                seenDecorIDs[data.decorID] = true
                uniqueDecorIDs = uniqueDecorIDs + 1
            end
        else
            noDecorID = noDecorID + 1
        end
    end

    print("|cffffd100DecorVendorData Counts|r")
    print("Active itemIDs:", total)
    print("Unique decorIDs:", uniqueDecorIDs)
    print("Missing decorID:", noDecorID)
end

function DVD.DebugCatalogMatch()
    if not C_HousingCatalog or not C_HousingCatalog.GetCatalogEntryInfoByRecordID then
        print("|cffff4040DecorVendorData: Housing Catalog API is not available.|r")
        return
    end

    local checked = 0
    local found = 0
    local missing = 0

    for itemID, data in pairs(DVD.ActiveItems or {}) do
        if data.decorID then
            checked = checked + 1

            local ok, info = pcall(C_HousingCatalog.GetCatalogEntryInfoByRecordID, 1, data.decorID, true)

            if ok and info then
                found = found + 1
            else
                missing = missing + 1
            end
        end
    end

    print("|cffffd100DecorVendorData Catalog Match|r")
    print("Checked decorIDs:", checked)
    print("Found in this catalog:", found)
    print("Missing/hidden on this build:", missing)
end

-- ============================================================
-- Group Counts
-- ============================================================

function DVD.DebugGroup(groupName)
    if not groupName or groupName == "" then
        print("|cffffcc00Usage:|r /dvdata group Midnight")
        return
    end

    local groups = DVD.ActiveItemsByExpansion or {}
    local group = groups[groupName]

    if not group then
        local lowerWanted = string.lower(groupName)

        for name, data in pairs(groups) do
            if string.lower(name) == lowerWanted then
                groupName = name
                group = data
                break
            end
        end
    end

    if not group then
        print("|cffff4040DecorVendorData: No group found named:|r", groupName)
        return
    end

    local count = 0

    for _ in pairs(group) do
        count = count + 1
    end

    print("|cffffd100DecorVendorData Group|r", groupName, "=", count, "items")
end

function DVD.DebugGroups()
    print("|cffffd100DecorVendorData Groups|r")

    local names = {}

    for groupName in pairs(DVD.ActiveItemsByExpansion or {}) do
        table.insert(names, groupName)
    end

    table.sort(names)

    for _, groupName in ipairs(names) do
        local count = 0
        local group = DVD.ActiveItemsByExpansion[groupName]

        for _ in pairs(group or {}) do
            count = count + 1
        end

        print(groupName .. ":", count)
    end
end

-- ============================================================
-- Hidden / Missing Catalog Items
-- ============================================================

function DVD.GetHiddenCatalogItems()
    local hidden = {}

    if not C_HousingCatalog or not C_HousingCatalog.GetCatalogEntryInfoByRecordID then
        print("|cffff4040DecorVendorData: Housing Catalog API is not available.|r")
        return hidden
    end

    for itemID, data in pairs(DVD.ActiveItems or {}) do
        if data.decorID then
            local ok, info = pcall(C_HousingCatalog.GetCatalogEntryInfoByRecordID, 1, data.decorID, true)

            if not ok or not info then
                table.insert(hidden, {
                    itemID = itemID,
                    data = data,
                    group = data.expansion or (DVD.ActiveItemSources and DVD.ActiveItemSources[itemID]) or "Unknown",
                })

                if C_Item and C_Item.RequestLoadItemDataByID then
                    C_Item.RequestLoadItemDataByID(itemID)
                end
            end
        end
    end

    table.sort(hidden, function(a, b)
        if a.group == b.group then
            return a.itemID < b.itemID
        end

        return tostring(a.group) < tostring(b.group)
    end)

    return hidden
end

function DVD.DebugHiddenCatalogItems(page)
    page = tonumber(page) or 1

    local hidden = DVD.GetHiddenCatalogItems()
    local perPage = 20
    local total = #hidden
    local totalPages = math.max(1, math.ceil(total / perPage))

    if page < 1 then
        page = 1
    end

    if page > totalPages then
        page = totalPages
    end

    print("|cffffd100DecorVendorData Hidden Catalog Items|r")
    print("Hidden/missing on this build:", total, "Page:", page .. "/" .. totalPages)

    local startIndex = ((page - 1) * perPage) + 1
    local endIndex = math.min(startIndex + perPage - 1, total)

    for i = startIndex, endIndex do
        local entry = hidden[i]
        local itemName = C_Item and C_Item.GetItemNameByID and C_Item.GetItemNameByID(entry.itemID)

        itemName = itemName or ("itemID " .. entry.itemID)

        print(
            i .. ".",
            "|cff66ccff" .. tostring(entry.group) .. "|r",
            itemName,
            "itemID:", entry.itemID,
            "decorID:", entry.data.decorID,
            "source:", tostring(entry.data.source)
        )
    end

    if page < totalPages then
        print("|cffffcc00Next page:|r /dvdata hidden " .. (page + 1))
    end
end

function DVD.DebugHiddenByGroup()
    local hidden = DVD.GetHiddenCatalogItems()
    local counts = {}
    local names = {}

    for _, entry in ipairs(hidden) do
        local group = entry.group or "Unknown"

        if not counts[group] then
            table.insert(names, group)
            counts[group] = 0
        end

        counts[group] = counts[group] + 1
    end

    table.sort(names)

    print("|cffffd100DecorVendorData Hidden By Group|r")

    for _, group in ipairs(names) do
        print(group .. ":", counts[group])
    end
end

-- ============================================================
-- Lua Export Helpers
-- ============================================================

local function DVD_TableKeySort(a, b)
    return tostring(a) < tostring(b)
end

local function DVD_SerializeValue(value)
    local valueType = type(value)

    if valueType == "string" then
        return string.format("%q", value)
    elseif valueType == "number" or valueType == "boolean" then
        return tostring(value)
    elseif valueType == "table" then
        local parts = {}
        local isArray = true
        local maxIndex = 0

        for k in pairs(value) do
            if type(k) ~= "number" then
                isArray = false
                break
            end

            if k > maxIndex then
                maxIndex = k
            end
        end

        if isArray then
            for i = 1, maxIndex do
                table.insert(parts, DVD_SerializeValue(value[i]))
            end

            return "{" .. table.concat(parts, ", ") .. "}"
        end

        local keys = {}

        for k in pairs(value) do
            if k ~= "expansion" then
                table.insert(keys, k)
            end
        end

        table.sort(keys, DVD_TableKeySort)

        for _, k in ipairs(keys) do
            local keyText

            if type(k) == "string" and string.match(k, "^[%a_][%w_]*$") then
                keyText = k
            else
                keyText = "[" .. DVD_SerializeValue(k) .. "]"
            end

            table.insert(parts, keyText .. " = " .. DVD_SerializeValue(value[k]))
        end

        return "{ " .. table.concat(parts, ", ") .. " }"
    end

    return "nil"
end

function DVD.BuildHiddenCatalogExport()
    local hidden = DVD.GetHiddenCatalogItems()
    local lines = {}

    table.insert(lines, "-- ============================================================")
    table.insert(lines, "-- DecorVendorData hidden/missing catalog items")
    table.insert(lines, "-- These are in DecorVendorData.ActiveItems but hidden/missing")
    table.insert(lines, "-- from the Housing Catalog API on this current build/realm.")
    table.insert(lines, "-- Total hidden/missing: " .. tostring(#hidden))
    table.insert(lines, "-- ============================================================")
    table.insert(lines, "")

    local lastGroup

    for _, entry in ipairs(hidden) do
        local itemID = entry.itemID
        local data = entry.data
        local group = entry.group or "Unknown"

        if group ~= lastGroup then
            if lastGroup then
                table.insert(lines, "")
            end

            table.insert(lines, "-- " .. tostring(group))
            lastGroup = group
        end

        local itemName = C_Item and C_Item.GetItemNameByID and C_Item.GetItemNameByID(itemID)
        local nameComment = itemName and (" -- " .. itemName) or ""

        table.insert(lines, "[" .. itemID .. "] = " .. DVD_SerializeValue(data) .. "," .. nameComment)
    end

    return table.concat(lines, "\n")
end

-- ============================================================
-- Copy Box Frame
-- ============================================================

function DVD.ShowCopyBox(title, text)
    if not DVD.CopyFrame then
        local frame = CreateFrame("Frame", "DecorVendorDataCopyFrame", UIParent, "BackdropTemplate")
        frame:SetSize(850, 620)
        frame:SetPoint("CENTER")
        frame:SetFrameStrata("DIALOG")
        frame:SetFrameLevel(500)
        frame:SetMovable(true)
        frame:EnableMouse(true)
        frame:RegisterForDrag("LeftButton")
        frame:SetScript("OnDragStart", frame.StartMoving)
        frame:SetScript("OnDragStop", frame.StopMovingOrSizing)

        frame:SetBackdrop({
            bgFile = "Interface\\Buttons\\WHITE8x8",
            edgeFile = "Interface\\Buttons\\WHITE8x8",
            edgeSize = 1,
        })

        frame:SetBackdropColor(0.02, 0.015, 0.035, 0.98)
        frame:SetBackdropBorderColor(0.9, 0.7, 0.2, 1)

        frame.title = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
        frame.title:SetPoint("TOP", 0, -14)
        frame.title:SetText("|cffffd100DecorVendorData Export|r")

        frame.instructions = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
        frame.instructions:SetPoint("TOP", frame.title, "BOTTOM", 0, -6)
        frame.instructions:SetText("|cffccccccClick inside the box, press Ctrl+A, then Ctrl+C.|r")

        local scroll = CreateFrame("ScrollFrame", nil, frame, "UIPanelScrollFrameTemplate")
        scroll:SetPoint("TOPLEFT", 18, -58)
        scroll:SetPoint("BOTTOMRIGHT", -32, 48)

        local editBox = CreateFrame("EditBox", nil, scroll)
        editBox:SetMultiLine(true)
        editBox:SetAutoFocus(false)
        editBox:SetFontObject(ChatFontNormal)
        editBox:SetWidth(780)
        editBox:SetTextInsets(8, 8, 8, 8)

        editBox:SetScript("OnEscapePressed", function(self)
            self:ClearFocus()
        end)

        editBox:SetScript("OnEditFocusGained", function(self)
            self:HighlightText()
        end)

        scroll:SetScrollChild(editBox)

        frame.editBox = editBox
        frame.scroll = scroll

        frame.selectAll = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
        frame.selectAll:SetSize(110, 24)
        frame.selectAll:SetPoint("BOTTOMLEFT", 18, 14)
        frame.selectAll:SetText("Select All")
        frame.selectAll:SetScript("OnClick", function()
            frame.editBox:SetFocus()
            frame.editBox:HighlightText()
        end)

        frame.close = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
        frame.close:SetSize(90, 24)
        frame.close:SetPoint("BOTTOMRIGHT", -18, 14)
        frame.close:SetText("Close")
        frame.close:SetScript("OnClick", function()
            frame:Hide()
        end)

        DVD.CopyFrame = frame
    end

    DVD.CopyFrame.title:SetText(title or "|cffffd100DecorVendorData Export|r")
    DVD.CopyFrame.editBox:SetText(text or "")
    DVD.CopyFrame.editBox:SetCursorPosition(0)
    DVD.CopyFrame.editBox:HighlightText()
    DVD.CopyFrame:Show()
end

function DVD.OpenHiddenCatalogCopyBox()
    local text = DVD.BuildHiddenCatalogExport()
    DVD.ShowCopyBox("|cffffd100Hidden Catalog Items|r", text)
end

-- ============================================================
-- 🏛️ PTR LIVE MERCHANDISE SCRAPER & DATA SYNC MATRIX
-- ============================================================

function DVD.ScrapeAndCrossReferenceCatalog(msg)
    if not MerchantFrame or not MerchantFrame:IsShown() then
        print("❌ Error: You must talk to a housing vendor and open their shop window first!")
        return
    end

    local lowerMsg = string.lower(msg or "")

    -- Identify targeted merchant NPC metadata properties safely
    local npcName = UnitName("target") or "Unknown Vendor"
    local npcID = 0
    local guid = UnitGUID("target")
    if guid then
        local parts = { strsplit("-", guid) }
        if parts[1] == "Creature" or parts[1] == "Vehicle" then
            npcID = tonumber(parts[6]) or 0
        end
    end
    if npcID == 0 then npcID = 255222 end

    -- Reset session list if switching to a brand new NPC or typing clear
    if DVD.ScraperSessionNPC ~= npcID or lowerMsg == "clear" or lowerMsg == "reset" then
        DVD.ScraperSessionDump = {}
        DVD.ScraperSessionNPC = npcID
    end

    -- Determine slot count variables matching active engine configurations
    local numMerchantItems = 0
    if C_MerchantFrame and C_MerchantFrame.GetNumMerchantItems then
        numMerchantItems = C_MerchantFrame.GetNumMerchantItems() or 0
    elseif _G.GetNumMerchantItems then
        numMerchantItems = _G.GetNumMerchantItems() or 0
    end

    local currentPage = 1
    if C_MerchantFrame and C_MerchantFrame.GetPage then
        currentPage = C_MerchantFrame.GetPage() or 1
    elseif MerchantFrame and MerchantFrame.page then
        currentPage = MerchantFrame.page or 1
    end

    local itemsPerPage = MERCHANT_ITEMS_PER_PAGE or 10
    if itemsPerPage < 10 then itemsPerPage = 10 end

    local startSlot = ((currentPage - 1) * itemsPerPage) + 1
    local endSlot = startSlot + itemsPerPage - 1

    local newlyScrapedThisPage = 0
    local fileMatchesCount = 0

    for i = startSlot, endSlot do
        local itemLink
        if C_MerchantFrame and C_MerchantFrame.GetMerchantItemLink then
            itemLink = C_MerchantFrame.GetMerchantItemLink(i)
        elseif _G.GetMerchantItemLink then
            itemLink = _G.GetMerchantItemLink(i)
        end
        
        if itemLink then
            local itemID = tonumber(itemLink:match("item:(%d+)"))
            
            if itemID and not DVD.ScraperSessionDump[itemID] then
                local name, price, currencyID = "Unknown Item", 0, nil
                
                -- Protected evaluation pass-through
                pcall(function()
                    if C_MerchantFrame and C_MerchantFrame.GetMerchantItemInfo then
                        local info = C_MerchantFrame.GetMerchantItemInfo(i)
                        if info then
                            name = info.name or name
                            price = info.price or 0
                            currencyID = info.currencyID
                        end
                    elseif _G.GetMerchantItemInfo then
                        name, _, price, _, _, _, _, currencyID = _G.GetMerchantItemInfo(i)
                    end
                end)

                -- Query backend core database fields to gather true zone definitions text
                local catalogZone = "Unknown Zone"
                
                if C_Housing and C_Housing.GetDecorInfoByItemID then
                    local decorInfo = C_Housing.GetDecorInfoByItemID(itemID)
                    if decorInfo and decorInfo.decorID then
                        catalogZone = decorInfo.sourceZone or decorInfo.obtainedFrom or "Catalog Record Bound"
                    else
                        catalogZone = "⚠️ NOT IN CATALOG ⚠️"
                    end
                end

                -- Cross-reference against your updated layout database structures (Deep Scan Arrays)
                local fileRecord = DVD.ActiveItems and DVD.ActiveItems[itemID]
                local catalogComment = ""
                
                if fileRecord then
                    catalogComment = string.format(" -- ✅ MATCHED IN FILES (decorID: %s)", tostring(fileRecord.decorID or "???"))
                    fileMatchesCount = fileMatchesCount + 1
                else
                    catalogComment = " -- 🚨 GAP DETECTED! Unmarked or missing from files list arrays!"
                end

                -- Auto-format pricing definitions properties string layouts safely
                local priceString = ""
                price = price or 0
                if currencyID and price > 0 then
                    priceString = string.format("price = { currencyID = %d, amount = %d }, ", currencyID, price)
                elseif price > 0 then
                    local goldAmount = math.floor(price / 10000)
                    priceString = string.format('priceText = "%d ", ', goldAmount)
                end

                local row = string.format(
                    '    [%d] = {\n        [%d] = { %sdisplayZone = "%s" },%s\n    },',
                    itemID, npcID, priceString, catalogZone, catalogComment
                )
                
                DVD.ScraperSessionDump[itemID] = row
                newlyScrapedThisPage = newlyScrapedThisPage + 1
            end
        end
    end

    -- Combine tracking dictionary indexes back to consecutive ledger strings
    local finalDump = {}
    table.insert(finalDump, "    -- ============================================================")
    table.insert(finalDump, string.format("    -- 🏛️ BLIZZARD HOUSING CATALOG SYNC: %s (NPC ID: %d)", npcName:upper(), npcID))
    table.insert(finalDump, "    -- ============================================================")

    local absoluteTotalCount = 0
    for _, formattedRow in pairs(DVD.ScraperSessionDump) do
        table.insert(finalDump, formattedRow)
        absoluteTotalCount = absoluteTotalCount + 1
    end

    local finalOutputText = table.concat(finalDump, "\n")

    -- Stream outputs directly into visual copy popup frames
    if absoluteTotalCount > 0 then
        if DVD.ShowCopyBox then
            DVD.ShowCopyBox(string.format("%s DATA CODES", npcName:upper()), finalOutputText)
            
            local boxFrame = _G["DecorVendorDataCopyFrame"] or DVD.CopyFrame
            if boxFrame and boxFrame:IsShown() then
                local editBox = boxFrame.editBox
                if editBox and editBox.SetText then
                    editBox:SetText(finalOutputText)
                    editBox:HighlightText()
                end
            end
        else
            print(finalOutputText)
        end
        print(string.format("|cff30d580[Catalog Sync]|r Page %d processed! Added %d items. Total Session: |cffbfa0ff%d items|r.", currentPage, newlyScrapedThisPage, absoluteTotalCount))
    else
        print("|cffff4040[Catalog Error]|r No items processed. Turn the page view and re-run your macro command option.")
    end
end

-- ============================================================
-- Slash Command Global Dispatch Engine Router
-- ============================================================

SLASH_DECORVENDORDATA1 = "/dvdata"
SlashCmdList["DECORVENDORDATA"] = function(msg)
    msg = msg or ""
    local lower = string.lower(msg)

    if lower == "count" or lower == "counts" then
        DVD.DebugCounts()
    elseif lower == "catalog" then
        DVD.DebugCatalogMatch()
    elseif lower == "groups" then
        DVD.DebugGroups()
    elseif lower == "hidden groups" or lower == "hiddengroups" then
        DVD.DebugHiddenByGroup()
    elseif string.match(lower, "^hidden%s*%d*") then
        local page = string.match(msg, "^hidden%s*(%d*)$")
        DVD.DebugHiddenCatalogItems(page)
    elseif string.match(lower, "^group%s+") then
        local groupName = string.match(msg, "^group%s+(.+)$")
        DVD.DebugGroup(groupName)
    elseif lower == "target" or lower == "vendor" or lower == "scrape" or lower == "" or lower == "check" or lower == "ptr" or lower == "compare" or lower == "faction" then
        DVD.ScrapeAndCrossReferenceCatalog(msg)
    else
        print("|cffffd100DecorVendorData Commands|r")
        print("/dvdata count")
        print("/dvdata catalog")
        print("/dvdata groups")
        print("/dvdata group Midnight")
        print("/dvdata hidden")
        print("/dvdata hidden 2")
        print("/dvdata hidden groups")
        print("/dvdata target — Scrape/Verify Active Merchant Storefront")
        print("/dvhidden")
    end
end

SLASH_DECORVENDORHIDDEN1 = "/dvhidden"
SlashCmdList["DECORVENDORHIDDEN"] = function()
    DVD.OpenHiddenCatalogCopyBox()
end