--[[
============================================================================
ETB - CommunitySetLoader.lua
============================================================================
Loads 778 MoP-validated community sets from CommunitySets.lua and resolves
sourceIDs at runtime via C_TransmogCollection APIs.

Provides the same interface pattern as ETB.CustomSets so SetsUI can query
community sets using GetAllSets(), GetSet(), GetSetCompletion().

Data format in CommunitySets.lua:
  { name, armorType(1-5), expansion(0-4), filterType(1-7),
    items = { [invSlot] = { itemID, visualID } } }
============================================================================
]]--

local ADDON_NAME, NS = ...
local ETB = NS.ETB
if not ETB then return end

ETB.CommunitySets = ETB.CommunitySets or {}
local CS = ETB.CommunitySets

-- ============================================================================
-- CONSTANTS
-- ============================================================================

local ARMOR_NAMES = { [1] = "CLOTH", [2] = "LEATHER", [3] = "MAIL", [4] = "PLATE", [5] = "COSMETIC", [13] = "COSMETIC" }
local ARMOR_DISPLAY = { [1] = "Cloth", [2] = "Leather", [3] = "Mail", [4] = "Plate", [5] = "Cosmetic", [13] = "Cosmetic" }
local EXP_NAMES = { [0] = "Classic", [1] = "TBC", [2] = "Wrath", [3] = "Cata", [4] = "MoP" }
local FILTER_NAMES = {
    [1] = "Dungeon", [2] = "Tier Recolor", [3] = "Quest",
    [4] = "World/Crafted", [5] = "Holiday", [6] = "Misc", [7] = "Low-Level",
}

local CLASS_ARMOR = {
    MAGE = "CLOTH", WARLOCK = "CLOTH", PRIEST = "CLOTH",
    ROGUE = "LEATHER", DRUID = "LEATHER", MONK = "LEATHER",
    HUNTER = "MAIL", SHAMAN = "MAIL",
    WARRIOR = "PLATE", PALADIN = "PLATE", DEATHKNIGHT = "PLATE",
}

-- ============================================================================
-- STATE
-- ============================================================================

CS.sets = {}
CS.loaded = false
CS.playerArmor = "CLOTH"
CS.resolveComplete = false

-- ============================================================================
-- INITIALIZATION
-- ============================================================================

function CS:Initialize()
    local _, playerClass = UnitClass("player")
    self.playerArmor = CLASS_ARMOR[playerClass] or "CLOTH"

    self:LoadData()

    -- Start async source resolution after game has fully settled
    -- Delay longer to avoid competing with other init work
    C_Timer.After(5, function()
        self:ResolveAllSources()
    end)
end

function CS:LoadData()
    if self.loaded then return end
    if not ETB.CommunitySetData then
        ETB:Debug("CommunitySets", "CommunitySetData not loaded yet")
        return
    end

    local count = 0
    for i, raw in ipairs(ETB.CommunitySetData) do
        local name = raw[1]
        local armorType = raw[2]
        local expansion = raw[3]
        local filterType = raw[4]
        local rawItems = raw[5]

        local armorKey = ARMOR_NAMES[armorType] or "CLOTH"
        local setID = "COMMUNITY_" .. i

        -- Include ALL items unconditionally — names, icons, and collection
        -- status are resolved async by ResolveAllSources()
        local items = {}
        local pieceCount = 0
        for invSlot, itemData in pairs(rawItems) do
            items[invSlot] = {
                itemID = itemData[1],
                visualID = itemData[2],
                sourceID = 0,
                name = nil,
                icon = nil,
                quality = 2,
                isCollected = false,
            }
            pieceCount = pieceCount + 1
        end

        -- Skip sets with no items (broken entries)
        if pieceCount > 0 then
            self.sets[setID] = {
                id = setID,
                name = name,
                armorType = armorKey,
                armorDisplay = ARMOR_DISPLAY[armorType] or "Unknown",
                expansion = expansion,
                expansionName = EXP_NAMES[expansion] or "Unknown",
                filterType = filterType,
                filterName = FILTER_NAMES[filterType] or "Unknown",
                source = string.format("%s %s", EXP_NAMES[expansion] or "", FILTER_NAMES[filterType] or ""),
                items = items,
                pieceCount = pieceCount,
                collectedCount = 0,
                isCommunitySet = true,
                isUsable = (armorKey == self.playerArmor) or (armorType == 5) or (armorType == 13),
            }
            count = count + 1
        end
    end

    self.loaded = true
    ETB:Debug("CommunitySets", count .. " community sets loaded")
end

-- ============================================================================
-- RUNTIME SOURCE RESOLUTION (async batch)
-- ============================================================================

function CS:ResolveAllSources()
    if not self.loaded then return end

    local queue = {}
    for setID, set in pairs(self.sets) do
        for invSlot, item in pairs(set.items) do
            table.insert(queue, { set = set, slot = invSlot, item = item })
        end
    end

    local index = 0
    local BATCH_SIZE = 8           -- Max items per frame (was 25 — way too many)
    local TIME_BUDGET = 0.006      -- 6ms max per frame to avoid hitches
    local resolveFrame = CreateFrame("Frame")
    local getTime = debugprofilestop or function() return 0 end
    local itemsReplaced = 0        -- Track post-MoP item replacements

    resolveFrame:SetScript("OnUpdate", function(self, elapsed)
        local startTime = getTime()
        local processed = 0

        while processed < BATCH_SIZE and index < #queue do
            -- Time check every item to stay under budget
            if processed > 0 and (getTime() - startTime) > (TIME_BUDGET * 1000) then
                break
            end

            index = index + 1
            local entry = queue[index]
            local item = entry.item

            -- 1. Load item info FIRST via sync API (no network, no hitch)
            local itemExists = false
            if GetItemInfoInstant then
                local iName, _, _, _, iIcon = GetItemInfoInstant(item.itemID)
                if iName then
                    item.name = iName
                    item.icon = iIcon
                    itemExists = true
                end
            end

            -- 2. Resolve source from visualID (sync API, no network)
            local resolvedVID = ETB:ResolveVisualID(item)
            if resolvedVID
               and C_TransmogCollection and C_TransmogCollection.GetAppearanceSources then
                local ok, sources = pcall(C_TransmogCollection.GetAppearanceSources, resolvedVID)
                if ok and sources then
                    local bestSourceID = nil
                    local bestItemID = nil
                    local bestCollected = false
                    local bestQuality = nil
                    
                    for _, src in ipairs(sources) do
                        local sourceID = type(src) == "table" and src.sourceID or src
                        if sourceID then
                            local srcOK, srcInfo = pcall(C_TransmogCollection.GetSourceInfo, sourceID)
                            if srcOK and srcInfo then
                                -- Always track a valid replacement itemID
                                local srcItemID = srcInfo.itemID
                                
                                if srcInfo.isCollected then
                                    -- Collected source: best choice
                                    bestSourceID = sourceID
                                    bestCollected = true
                                    bestQuality = srcInfo.quality
                                    if srcItemID and srcItemID > 0 then
                                        bestItemID = srcItemID
                                    end
                                    break  -- Found collected, stop searching
                                elseif not bestSourceID then
                                    -- First valid source as fallback
                                    bestSourceID = sourceID
                                    bestQuality = srcInfo.quality
                                    if srcItemID and srcItemID > 0 then
                                        bestItemID = srcItemID
                                    end
                                end
                            end
                        end
                    end
                    
                    if bestSourceID then
                        item.sourceID = bestSourceID
                        if bestCollected then item.isCollected = true end
                        if type(bestQuality) == "number" then item.quality = bestQuality end
                    end
                    
                    -- KEY FIX: If original itemID doesn't exist in MoP client,
                    -- replace it with a valid itemID from the same appearance
                    if not itemExists and bestItemID and bestItemID > 0 then
                        local replaceName, _, _, _, replaceIcon = GetItemInfoInstant(bestItemID)
                        if replaceName then
                            item.itemID = bestItemID
                            item.name = replaceName
                            item.icon = replaceIcon
                            itemExists = true
                            itemsReplaced = itemsReplaced + 1
                        end
                    end
                end
            end

            -- 2b. Fallback: if step 2 yielded no collected source,
            --     try via itemID → GetItemInfo → fresh runtime visualID
            --     (Retail visualID may differ from MoP Classic)
            if not item.isCollected and item.itemID
               and C_TransmogCollection and C_TransmogCollection.GetItemInfo then
                local iOK, iVisualID = pcall(C_TransmogCollection.GetItemInfo, item.itemID)
                if iOK and iVisualID and iVisualID > 0 then
                    local ok2, sources2 = pcall(C_TransmogCollection.GetAppearanceSources, iVisualID)
                    if ok2 and sources2 then
                        for _, src in ipairs(sources2) do
                            local sourceID = type(src) == "table" and src.sourceID or src
                            if sourceID then
                                local srcOK, srcInfo = pcall(C_TransmogCollection.GetSourceInfo, sourceID)
                                if srcOK and srcInfo then
                                    if srcInfo.isCollected then
                                        item.sourceID = sourceID
                                        item.isCollected = true
                                        if type(srcInfo.quality) == "number" then item.quality = srcInfo.quality end
                                        if iVisualID ~= item.visualID then
                                            item.visualID = iVisualID
                                        end
                                        -- Replace invalid itemID from this path
                                        if not itemExists and srcInfo.itemID and srcInfo.itemID > 0 then
                                            local rName, _, _, _, rIcon = GetItemInfoInstant(srcInfo.itemID)
                                            if rName then
                                                item.itemID = srcInfo.itemID
                                                item.name = rName
                                                item.icon = rIcon
                                                itemExists = true
                                                itemsReplaced = itemsReplaced + 1
                                            end
                                        end
                                        break
                                    elseif item.sourceID == 0 then
                                        item.sourceID = sourceID
                                        if type(srcInfo.quality) == "number" then item.quality = srcInfo.quality end
                                        -- Replace invalid itemID even for uncollected
                                        if not itemExists and srcInfo.itemID and srcInfo.itemID > 0 then
                                            local rName, _, _, _, rIcon = GetItemInfoInstant(srcInfo.itemID)
                                            if rName then
                                                item.itemID = srcInfo.itemID
                                                item.name = rName
                                                item.icon = rIcon
                                                itemExists = true
                                                itemsReplaced = itemsReplaced + 1
                                            end
                                        end
                                    end
                                end
                            end
                        end
                    end
                end
            end

            -- 2c. Fallback: check PlayerHasTransmog if available
            if not item.isCollected and item.sourceID and item.sourceID > 0 then
                if C_TransmogCollection.PlayerHasTransmog then
                    local hasOK, hasTmog = pcall(C_TransmogCollection.PlayerHasTransmog, item.sourceID)
                    if hasOK and hasTmog then
                        item.isCollected = true
                    end
                end
                if not item.isCollected and C_TransmogCollection.PlayerHasTransmogItemModifiedAppearance then
                    local hasOK, hasTmog = pcall(C_TransmogCollection.PlayerHasTransmogItemModifiedAppearance, item.sourceID)
                    if hasOK and hasTmog then
                        item.isCollected = true
                    end
                end
            end

            -- 3. Try full GetItemInfo only if Instant didn't resolve name
            --    This is async and can trigger server requests, so do it sparingly
            if not item.name or type(item.name) == "number" then
                local name, link, quality, _, _, _, _, _, _, icon = GetItemInfo(item.itemID)
                if name then
                    item.name = name
                    item.icon = icon
                    if type(quality) == "number" then item.quality = quality end
                end
            end

            processed = processed + 1
        end

        -- Update collection counts every ~400 items or at end
        if index % 400 == 0 or index >= #queue then
            CS:UpdateCollectionCounts()
        end

        if index >= #queue then
            self:SetScript("OnUpdate", nil)
            CS.resolveComplete = true
            CS:UpdateCollectionCounts()
            ETB:Debug("CommunitySets", string.format("Source resolution complete (%d items, %d post-MoP items replaced)", index, itemsReplaced))
            if itemsReplaced > 0 then
                ETB:Debug("CommunitySets", string.format("Replaced %d post-MoP itemIDs with valid MoP alternatives via visualID", itemsReplaced))
            end

            -- Refresh SetsUI after community resolution completes
            if ETB.SetsUI and ETB.SetsUI.frame and ETB.SetsUI.frame:IsShown() then
                ETB.SetsUI:RefreshSetList()
            end
            
            -- Also refresh TransmogUI sets panel if it's open
            if ETB.TransmogUI and ETB.TransmogUI.frame and ETB.TransmogUI.frame:IsShown() then
                if ETB.TransmogUI.RefreshSets then
                    ETB.TransmogUI:RefreshSets()
                end
            end
        end
    end)
end

function CS:UpdateCollectionCounts()
    if not C_TransmogCollection then return end
    
    for setID, set in pairs(self.sets) do
        local collected = 0
        for _, item in pairs(set.items) do
            -- Re-check live collection status via API
            local wasCollected = item.isCollected
            
            -- Method 1: sourceID-based check (most reliable)
            if item.sourceID and item.sourceID > 0 then
                if C_TransmogCollection.PlayerHasTransmogItemModifiedAppearance then
                    local ok, result = pcall(C_TransmogCollection.PlayerHasTransmogItemModifiedAppearance, item.sourceID)
                    if ok and result then
                        item.isCollected = true
                    end
                end
            end
            
            -- Method 2: appearance sources check (catches shared appearances)
            if not item.isCollected and item.visualID and item.visualID > 0 then
                if C_TransmogCollection.GetAppearanceSources then
                    local ok, sources = pcall(C_TransmogCollection.GetAppearanceSources, item.visualID)
                    if ok and sources then
                        for _, src in ipairs(sources) do
                            local sid = type(src) == "table" and src.sourceID or src
                            if sid then
                                local siOK, si = pcall(C_TransmogCollection.GetSourceInfo, sid)
                                if siOK and si and si.isCollected then
                                    item.isCollected = true
                                    if item.sourceID == 0 then
                                        item.sourceID = sid
                                    end
                                    break
                                end
                            end
                        end
                    end
                end
            end
            
            if item.isCollected then
                collected = collected + 1
            end
        end
        set.collectedCount = collected
    end
end

-- ============================================================================
-- QUERY API (matches CustomSets interface)
-- ============================================================================

function CS:GetAllSets()
    if not self.loaded then self:LoadData() end
    return self.sets
end

function CS:GetSet(setID)
    return self.sets[setID]
end

function CS:GetSetCompletion(setID)
    local set = self.sets[setID]
    if not set then return 0, 0 end
    return set.collectedCount, set.pieceCount
end

function CS:GetSetCount()
    local count = 0
    for _ in pairs(self.sets) do count = count + 1 end
    return count
end

function CS:IsSetUsable(setID)
    local set = self.sets[setID]
    if not set then return false end
    return set.isUsable
end

-- ============================================================================
-- INITIALIZATION HOOK
-- ============================================================================

local initFrame = CreateFrame("Frame")
initFrame:RegisterEvent("PLAYER_LOGIN")
initFrame:SetScript("OnEvent", function(self, event)
    C_Timer.After(2, function()
        CS:Initialize()
    end)
end)

ETB:Debug("CommunitySets", "CommunitySetLoader.lua loaded")
