--[[
============================================================================
ETB - ArmorData.lua (v12.6.1 Stable)
============================================================================
Armor data retrieval with both grouped and ungrouped modes.
============================================================================
]]--

local ADDON_NAME, NS = ...
local ETB = NS.ETB

ETB.ArmorData = {
    cache = {
        categories = {},
    },
    initialized = false,
    loadStats = {},
}

local AD = ETB.ArmorData

-- Armor subclass mapping
local ARMOR_SUBCLASS_MAP = {
    [1] = "CLOTH",
    [2] = "LEATHER", 
    [3] = "MAIL",
    [4] = "PLATE",
    [6] = "SHIELD",
}

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

function AD:Initialize()
    if self.initialized then return true end
    
    if not C_TransmogCollection then
        ETB:PrintError("C_TransmogCollection not available!")
        return false
    end
    
    self.initialized = true
    ETB:Debug("ArmorData", "ArmorData module initialized")
    return true
end

-- =============================================================================
-- CORE API METHODS
-- =============================================================================

function AD:GetCategoryAppearances(categoryID)
    if not ETB.BlizzardData then 
        return C_TransmogCollection.GetCategoryAppearances(categoryID) or {}
    end
    return ETB.BlizzardData:GetCategoryAppearances(categoryID)
end

function AD:GetAppearanceSources(visualID)
    if not ETB.BlizzardData then 
        return C_TransmogCollection.GetAppearanceSources(visualID) or {}
    end
    return ETB.BlizzardData:GetAppearanceSources(visualID)
end

-- =============================================================================
-- ITEM RETRIEVAL
-- =============================================================================

function AD:GetItemsForCategory(categoryID, options)
    if not categoryID or categoryID < 1 or categoryID > 11 then
        return {}
    end
    
    options = options or {}
    local limit = options.limit or 100000
    local onlyCollected = options.onlyCollected
    local onlyUncollected = options.onlyUncollected
    local onlyUsable = options.onlyUsable
    
    local groupByAppearance = ETB.Filtering and ETB.Filtering.filters.groupByAppearance or false
    
    -- Cache key (includes onlyUsable to prevent stale filtered results)
    local cacheKey = string.format("v4_%d_%s_%s_%s_%s", categoryID, 
        tostring(onlyCollected), tostring(onlyUncollected), tostring(groupByAppearance), tostring(onlyUsable))
    
    if self.cache.categories[cacheKey] then
        return self.cache.categories[cacheKey]
    end
    
    local appearances = self:GetCategoryAppearances(categoryID)
    if #appearances == 0 then return {} end
    
    -- Track stats
    local stats = {
        appearances = #appearances,
        sources = 0,
        items = 0,
        byArmorType = { CLOTH = 0, LEATHER = 0, MAIL = 0, PLATE = 0, OTHER = 0 },
    }
    
    local items = {}
    local itemsByVisualID = {}
    
    -- Process appearances
    for _, appearance in ipairs(appearances) do
        if #items >= limit then break end
        
        -- Collection filter at appearance level
        local skipAppearance = false
        if onlyCollected and not appearance.isCollected then
            skipAppearance = true
        elseif onlyUncollected and appearance.isCollected then
            skipAppearance = true
        end
        
        if not skipAppearance then
            local sources = self:GetAppearanceSources(appearance.visualID)
            stats.sources = stats.sources + #sources
            
            for _, source in ipairs(sources) do
                if #items >= limit then break end
                
                if source.itemID then
                    local _, _, _, equipLoc, itemIcon, classID, subclassID = GetItemInfoInstant(source.itemID)
                    
                    local armorType = nil
                    if classID == 4 then
                        armorType = ARMOR_SUBCLASS_MAP[subclassID]
                    end
                    
                    -- Filter by class usability (armor type must match player's primary)
                    -- Skip for cosmetic slots: Back(3), Shirt(5), Tabard(6)
                    local skipUsability = false
                    if onlyUsable and armorType then
                        local COSMETIC_CATS = { [3]=true, [5]=true, [6]=true }
                        if not COSMETIC_CATS[categoryID] then
                            if ETB.BlizzardData and not ETB.BlizzardData:CanPlayerWearArmorType(armorType) then
                                skipUsability = true
                            end
                        end
                    end
                    
                    if not skipUsability then
                    
                    -- Track stats
                    if armorType then
                        stats.byArmorType[armorType] = (stats.byArmorType[armorType] or 0) + 1
                    else
                        stats.byArmorType.OTHER = stats.byArmorType.OTHER + 1
                    end
                    
                    -- Build item from cached/instant data. Avoid mass GetItemInfo
                    -- requests while constructing the category list.
                    local cached = ETB.BlizzardData and ETB.BlizzardData.itemCache and ETB.BlizzardData.itemCache:Get(source.itemID)
                    local itemName = cached and cached.name
                    local itemLink = cached and cached.link
                    local quality = cached and cached.quality
                    
                    local item = {
                        itemID = source.itemID,
                        sourceID = source.sourceID,
                        visualID = appearance.visualID,
                        name = itemName or "Loading...",
                        link = itemLink,
                        quality = quality or 2,
                        icon = itemIcon or "Interface\\Icons\\INV_Misc_QuestionMark",
                        isCollected = source.isCollected or appearance.isCollected,
                        categoryID = categoryID,
                        armorType = armorType,
                        invType = equipLoc,
                    }
                    
                    -- Source enrichment can materialize large all-expansion data.
                    -- Keep low-memory rows lean and hydrate on demand in Filtering.
                    if not (ETB.IsLowMemoryMode and ETB:IsLowMemoryMode()) and ETB.HydrateItemSource then
                        ETB:HydrateItemSource(item)
                    end
                    
                    if groupByAppearance then
                        -- Group mode
                        local visualID = appearance.visualID
                        if not itemsByVisualID[visualID] then
                            itemsByVisualID[visualID] = {
                                items = {},
                                armorTypes = {},
                            }
                        end
                        table.insert(itemsByVisualID[visualID].items, item)
                        if armorType then
                            itemsByVisualID[visualID].armorTypes[armorType] = true
                        end
                    else
                        -- Ungrouped mode - add all items
                        items[#items + 1] = item
                        stats.items = stats.items + 1
                    end
                    
                    -- Request name if needed
                    if not itemName and ETB.RequestItemInfoThrottled then
                        ETB:RequestItemInfoThrottled(source.itemID)
                    end
                    end -- close skipUsability check
                end
            end
        end
    end
    
    -- If grouped mode, create final list
    if groupByAppearance then
        for visualID, data in pairs(itemsByVisualID) do
            if #items >= limit then break end
            
            -- Select best representative
            local selectedItem = nil
            for _, item in ipairs(data.items) do
                if item.isCollected then
                    selectedItem = item
                    break
                end
            end
            
            if not selectedItem then
                local bestQuality = -1
                for _, item in ipairs(data.items) do
                    local q = tonumber(item.quality) or 0
                    if q > bestQuality then
                        bestQuality = q
                        selectedItem = item
                    end
                end
            end
            
            if not selectedItem and #data.items > 0 then
                selectedItem = data.items[1]
            end
            
            if selectedItem then
                selectedItem.alternatives = data.items
                selectedItem.hasAlternatives = #data.items > 1
                selectedItem.alternativeCount = #data.items
                selectedItem.availableArmorTypes = data.armorTypes
                
                items[#items + 1] = selectedItem
                stats.items = stats.items + 1
            end
        end
    end
    
    -- Store stats and cache
    self.loadStats[categoryID] = stats
    self.cache.categories[cacheKey] = items
    
    return items
end

function AD:GetItemsForCategoryAsync(categoryID, options, callbacks)
    callbacks = callbacks or {}
    if not categoryID or categoryID < 1 or categoryID > 11 then
        if callbacks.onComplete then callbacks.onComplete({}) end
        return
    end

    options = options or {}
    local limit = options.limit or 100000
    local onlyCollected = options.onlyCollected
    local onlyUncollected = options.onlyUncollected
    local onlyUsable = options.onlyUsable
    local groupByAppearance = ETB.Filtering and ETB.Filtering.filters.groupByAppearance or false
    local cacheKey = string.format("v4_%d_%s_%s_%s_%s", categoryID,
        tostring(onlyCollected), tostring(onlyUncollected), tostring(groupByAppearance), tostring(onlyUsable))

    if self.cache.categories[cacheKey] then
        if callbacks.onProgress then callbacks.onProgress(1, 1, "cached") end
        if C_Timer and C_Timer.After then
            C_Timer.After(0, function()
                if callbacks.onComplete then callbacks.onComplete(self.cache.categories[cacheKey], true) end
            end)
        elseif callbacks.onComplete then
            callbacks.onComplete(self.cache.categories[cacheKey], true)
        end
        return
    end

    local appearances = self:GetCategoryAppearances(categoryID)
    if #appearances == 0 then
        if callbacks.onComplete then callbacks.onComplete({}) end
        return
    end

    local stats = {
        appearances = #appearances,
        sources = 0,
        items = 0,
        byArmorType = { CLOTH = 0, LEATHER = 0, MAIL = 0, PLATE = 0, OTHER = 0 },
    }
    local items = {}
    local itemsByVisualID = {}
    local appIndex = 1
    local currentAppearance, currentSources, sourceIndex
    local getTime = debugprofilestop or function() return 0 end
    local budgetMs = (options.budgetMs or 3.5)
    local COSMETIC_CATS = { [3]=true, [5]=true, [6]=true }

    local function processSource(appearance, source)
        if not source or not source.itemID then return end

        local _, _, _, equipLoc, itemIcon, classID, subclassID = GetItemInfoInstant(source.itemID)
        local armorType = nil
        if classID == 4 then
            armorType = ARMOR_SUBCLASS_MAP[subclassID]
        end

        local skipUsability = false
        if onlyUsable and armorType and not COSMETIC_CATS[categoryID] then
            if ETB.BlizzardData and not ETB.BlizzardData:CanPlayerWearArmorType(armorType) then
                skipUsability = true
            end
        end
        if skipUsability then return end

        if armorType then
            stats.byArmorType[armorType] = (stats.byArmorType[armorType] or 0) + 1
        else
            stats.byArmorType.OTHER = stats.byArmorType.OTHER + 1
        end

        local cached = ETB.BlizzardData and ETB.BlizzardData.itemCache and ETB.BlizzardData.itemCache:Get(source.itemID)
        local itemName = cached and cached.name
        local itemLink = cached and cached.link
        local quality = cached and cached.quality
        local item = {
            itemID = source.itemID,
            sourceID = source.sourceID,
            visualID = appearance.visualID,
            name = itemName or "Loading...",
            link = itemLink,
            quality = quality or 2,
            icon = itemIcon or "Interface\\Icons\\INV_Misc_QuestionMark",
            isCollected = source.isCollected or appearance.isCollected,
            categoryID = categoryID,
            armorType = armorType,
            invType = equipLoc,
        }

        if not (ETB.IsLowMemoryMode and ETB:IsLowMemoryMode()) and ETB.HydrateItemSource then
            ETB:HydrateItemSource(item)
        end

        if groupByAppearance then
            local visualID = appearance.visualID
            if not itemsByVisualID[visualID] then
                itemsByVisualID[visualID] = { items = {}, armorTypes = {} }
            end
            table.insert(itemsByVisualID[visualID].items, item)
            if armorType then itemsByVisualID[visualID].armorTypes[armorType] = true end
        else
            items[#items + 1] = item
            stats.items = stats.items + 1
        end

        if not itemName and ETB.RequestItemInfoThrottled then
            ETB:RequestItemInfoThrottled(source.itemID)
        end
    end

    local function complete()
        self.loadStats[categoryID] = stats
        self.cache.categories[cacheKey] = items
        if callbacks.onProgress then callbacks.onProgress(#appearances, #appearances, "complete") end
        if callbacks.onComplete then callbacks.onComplete(items, false) end
    end

    local function finish()
        if not groupByAppearance then
            complete()
            return
        end

        local groupKeys = {}
        for visualID in pairs(itemsByVisualID) do
            groupKeys[#groupKeys + 1] = visualID
        end

        local groupIndex = 1
        local function scheduleGroup(stepFn)
            if ETB.QueueWork then
                ETB:QueueWork(nil, function() stepFn(); return true end, { pauseInCombat = true, startupIdleOptions = { ignoreQuietWindow = true, pauseWhileMoving = false } })
            elseif C_Timer and C_Timer.After then
                C_Timer.After(0.01, stepFn)
            else
                stepFn()
            end
        end

        local function groupStep()
            if callbacks.cancelled and callbacks.cancelled() then return end
            local startTime = getTime()

            while groupIndex <= #groupKeys and #items < limit do
                local data = itemsByVisualID[groupKeys[groupIndex]]
                groupIndex = groupIndex + 1

                local selectedItem = nil
                for _, item in ipairs(data.items) do
                    if item.isCollected then
                        selectedItem = item
                        break
                    end
                end
                if not selectedItem then
                    local bestQuality = -1
                    for _, item in ipairs(data.items) do
                        local q = tonumber(item.quality) or 0
                        if q > bestQuality then
                            bestQuality = q
                            selectedItem = item
                        end
                    end
                end
                if not selectedItem and #data.items > 0 then
                    selectedItem = data.items[1]
                end
                if selectedItem then
                    selectedItem.alternatives = data.items
                    selectedItem.hasAlternatives = #data.items > 1
                    selectedItem.alternativeCount = #data.items
                    selectedItem.availableArmorTypes = data.armorTypes
                    items[#items + 1] = selectedItem
                    stats.items = stats.items + 1
                end

                if (getTime() - startTime) >= budgetMs then
                    if callbacks.onProgress then callbacks.onProgress(groupIndex - 1, #groupKeys, "grouping") end
                    scheduleGroup(groupStep)
                    return
                end
            end

            complete()
        end

        groupStep()
    end

    local function schedule(step)
        if ETB.QueueWork then
            ETB:QueueWork(nil, function() step(); return true end, { pauseInCombat = true, startupIdleOptions = { ignoreQuietWindow = true, pauseWhileMoving = false } })
        elseif C_Timer and C_Timer.After then
            C_Timer.After(0.01, step)
        else
            step()
        end
    end

    local function step()
        if callbacks.cancelled and callbacks.cancelled() then return end
        local startTime = getTime()

        while appIndex <= #appearances and #items < limit do
            if not currentAppearance then
                local appearance = appearances[appIndex]
                appIndex = appIndex + 1

                local skipAppearance = false
                if onlyCollected and not appearance.isCollected then
                    skipAppearance = true
                elseif onlyUncollected and appearance.isCollected then
                    skipAppearance = true
                end

                if not skipAppearance then
                    currentAppearance = appearance
                    currentSources = self:GetAppearanceSources(appearance.visualID)
                    stats.sources = stats.sources + #currentSources
                    sourceIndex = 1
                end
            end

            while currentAppearance and sourceIndex <= #currentSources and #items < limit do
                processSource(currentAppearance, currentSources[sourceIndex])
                sourceIndex = sourceIndex + 1

                if (getTime() - startTime) >= budgetMs then
                    if callbacks.onProgress then callbacks.onProgress(appIndex - 1, #appearances, "building") end
                    schedule(step)
                    return
                end
            end

            if currentAppearance and sourceIndex > #currentSources then
                currentAppearance = nil
                currentSources = nil
                sourceIndex = nil
            end

            if (getTime() - startTime) >= budgetMs then
                if callbacks.onProgress then callbacks.onProgress(appIndex - 1, #appearances, "building") end
                schedule(step)
                return
            end
        end

        finish()
    end

    schedule(step)
end

-- =============================================================================
-- STATISTICS
-- =============================================================================

function AD:GetCollectionStats(categoryID)
    if ETB.BlizzardData then
        return ETB.BlizzardData:GetCollectionStats(categoryID)
    end
    return { total = 0, collected = 0, usable = 0, percent = 0 }
end

function AD:GetLoadStats(categoryID)
    return self.loadStats[categoryID]
end

function AD:PrintLoadReport()
    ETB:Print("|cff00ff00=== ARMOR DATA LOAD REPORT ===|r")
    
    local totalAppearances = 0
    local totalItems = 0
    
    local catNames = {
        [1] = "Head", [2] = "Shoulder", [3] = "Back", [4] = "Chest", [5] = "Shirt",
        [6] = "Tabard", [7] = "Wrist", [8] = "Hands", [9] = "Waist", [10] = "Legs", [11] = "Feet"
    }
    
    for catID = 1, 11 do
        local stats = self.loadStats[catID]
        if stats then
            ETB:Print(string.format("  %s: %d appearances → %d items",
                catNames[catID] or catID, stats.appearances, stats.items or stats.sources))
            
            totalAppearances = totalAppearances + stats.appearances
            totalItems = totalItems + (stats.items or stats.sources)
        end
    end
    
    ETB:Print(string.format("|cffffd100TOTALS:|r %d appearances → %d items", totalAppearances, totalItems))
end

-- =============================================================================
-- CACHE MANAGEMENT
-- =============================================================================

function AD:InvalidateCache()
    wipe(self.cache.categories)
    wipe(self.loadStats)
    ETB:Debug("ArmorData", "Armor cache invalidated")
end

ETB:Debug("ArmorData", "ArmorData.lua loaded")
