--[[
Sets.lua v3.0 - COMPLETE API-BASED SYSTEM
==========================================
✅ PRODUCTION READY - Full C_TransmogSets Integration

CRITICAL DISCOVERY (November 17, 2025):
✅ C_TransmogSets API EXISTS in MoP Classic 2024!
✅ 694 sets automatically available from Blizzard
✅ validForCharacter provides automatic class filtering
✅ collected tracks completion status automatically
✅ Zero manual data entry required!

ARCHITECTURE:
- Primary: C_TransmogSets API (694 Blizzard sets)
- Secondary: Custom user-created sets (ETB_CustomSets)
- Integration: Links to Database.appearances via appearanceID
- Compliance: MoP_Transmog_Rules_FINAL_v2_3.md, ETB_LUA_DATA_REQUIREMENTS_v1_0_2.md

MoP CLASSIC 2024 API STRUCTURE:
GetAllSets() returns FULL TABLE OBJECTS (not numeric IDs like retail!)
Each set table contains:
  - setID: numeric identifier
  - name: set name
  - description: set description
  - classMask: bitfield for class restrictions
  - validForCharacter: boolean (automatic class filtering!)
  - collected: boolean (Blizzard tracks completion)
  - baseSetID: numeric (for variants/recolors)
  - expansionID: 0-4 (Vanilla-MoP)
  - patchID: patch number
  - favorite: boolean
  - uiOrder: display order
  - limitedTimeSet: boolean
  - label: variant label (Normal/Heroic/Mythic/Elite)
  - hiddenUntilCollected: boolean

NO GetSetInfo() NEEDED - All data in GetAllSets() result!

Version: 3.0.0
Build Date: November 17, 2025
Author: ETB Team
]]--

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

local Sets = {}
ETB.Sets = Sets

-- ============================================================================
-- VERSION INFO
-- ============================================================================

Sets.Version = "3.0.0"
Sets.BuildDate = "November 17, 2025"
Sets.Description = "API-Based Sets System - 694 Blizzard Sets"

-- ============================================================================
-- CLASS MASK DEFINITIONS
-- ============================================================================

-- MoP Classic uses bitfield for class restrictions (same as retail)
local CLASS_MASK = {
    WARRIOR     = 0x01,  -- 1
    PALADIN     = 0x02,  -- 2
    HUNTER      = 0x04,  -- 4
    ROGUE       = 0x08,  -- 8
    PRIEST      = 0x10,  -- 16
    DEATHKNIGHT = 0x20,  -- 32
    SHAMAN      = 0x40,  -- 64
    MAGE        = 0x80,  -- 128
    WARLOCK     = 0x100, -- 256
    MONK        = 0x200, -- 512 (MoP only)
    DRUID       = 0x400  -- 1024
}

-- Reverse lookup: classMask -> class names
local CLASS_NAMES = {
    [0x01]  = "WARRIOR",
    [0x02]  = "PALADIN",
    [0x04]  = "HUNTER",
    [0x08]  = "ROGUE",
    [0x10]  = "PRIEST",
    [0x20]  = "DEATHKNIGHT",
    [0x40]  = "SHAMAN",
    [0x80]  = "MAGE",
    [0x100] = "WARLOCK",
    [0x200] = "MONK",
    [0x400] = "DRUID"
}

-- ============================================================================
-- SET TYPE TAXONOMY
-- ============================================================================

local SET_TYPES = {
    TIER      = "Tier",           -- Blizzard tracks (T0-T16)
    PVP       = "PvP",           -- Blizzard tracks (S1-S14)
    CHALLENGE = "Challenge Mode", -- Blizzard tracks (MoP only)
    DUNGEON   = "Dungeon",       -- Blizzard tracks
    CUSTOM    = "Custom"         -- User-created
}

-- ============================================================================
-- DATA STORAGE
-- ============================================================================

local BLIZZARD_SETS = {}              -- Cache of Blizzard sets (indexed by setID)
local CUSTOM_SETS = {}                -- User-created sets
local SETS_BY_EXPANSION = {}          -- Sets grouped by expansion
local SETS_BY_CLASS = {}              -- Sets grouped by class
local APPEARANCE_TO_SETS = {}         -- Map appearanceID -> sets using it
local API_AVAILABLE = false           -- Is C_TransmogSets available?
local INITIALIZED = false             -- Has module been initialized?

-- ============================================================================
-- EXPANSION MAPPING
-- ============================================================================

local EXPANSION_NAMES = {
    [0] = "Classic",
    [1] = "The Burning Crusade",
    [2] = "Wrath of the Lich King",
    [3] = "Cataclysm",
    [4] = "Mists of Pandaria"
}

-- ============================================================================
-- API AVAILABILITY CHECK
-- ============================================================================

function Sets:CheckAPIAvailability()
    if C_TransmogSets and C_TransmogSets.GetAllSets then
        API_AVAILABLE = true
        ETB:Debug("Sets", "✅ C_TransmogSets API available")
        
        -- Test the API to make sure it works
        local testSets = C_TransmogSets.GetAllSets()
        if testSets and #testSets > 0 then
            ETB:Debug("Sets", string.format("✅ API test successful: %d sets available", #testSets))
        else
            ETB:Debug("Sets", "⚠️ API test: GetAllSets returned empty/nil")
        end
        
        return true
    else
        API_AVAILABLE = false
        ETB:Debug("Sets", "⚠️ C_TransmogSets API not available")
        return false
    end
end

-- ============================================================================
-- BLIZZARD SETS IMPORT (PRIMARY DATA SOURCE)
-- ============================================================================

function Sets:ImportBlizzardSets()
    if not API_AVAILABLE then
        ETB:Debug("Sets", "Cannot import - API not available")
        return 0
    end
    
    ETB:Debug("Sets", "Importing Blizzard sets...")
    
    -- Get all sets from Blizzard API
    -- CRITICAL: MoP Classic returns FULL TABLE OBJECTS, not numeric IDs!
    -- NOTE: We cache static data (names, types, etc) but query fresh for
    -- character-specific data (validForCharacter, collected) to support
    -- multi-character usage without re-initialization
    local allSets = C_TransmogSets.GetAllSets()
    
    if not allSets or #allSets == 0 then
        ETB:Debug("Sets", "⚠️ GetAllSets() returned no data")
        return 0
    end
    
    local imported = 0
    local playerClass = select(2, UnitClass("player"))
    
    -- Clear existing Blizzard sets
    wipe(BLIZZARD_SETS)
    wipe(SETS_BY_EXPANSION)
    wipe(SETS_BY_CLASS)
    
    -- Process each set
    for i, setData in ipairs(allSets) do
        -- CRITICAL: setData is already a FULL TABLE with all fields!
        -- NO need to call C_TransmogSets.GetSetInfo(setID)!
        
        if setData and setData.setID then
            local setID = setData.setID
            
            -- Store complete set data
            BLIZZARD_SETS[setID] = {
                setID = setID,
                name = setData.name or "Unknown Set",
                description = setData.description or "",
                classMask = setData.classMask or 0,
                validForCharacter = setData.validForCharacter or false,
                collected = setData.collected or false,
                baseSetID = setData.baseSetID,
                expansionID = setData.expansionID or 0,
                patchID = setData.patchID or 0,
                favorite = setData.favorite or false,
                uiOrder = setData.uiOrder or 0,
                limitedTimeSet = setData.limitedTimeSet or false,
                label = setData.label or "",
                hiddenUntilCollected = setData.hiddenUntilCollected or false,
                
                -- ETB additions
                source = "Blizzard",
                type = self:DetermineSetType(setData),
                items = {}  -- Will be populated on-demand
            }
            
            -- Index by expansion
            local expID = setData.expansionID or 0
            SETS_BY_EXPANSION[expID] = SETS_BY_EXPANSION[expID] or {}
            table.insert(SETS_BY_EXPANSION[expID], setID)
            
            -- Index by class (using classMask)
            if setData.classMask and setData.classMask > 0 then
                local classes = self:GetClassesFromMask(setData.classMask)
                for _, className in ipairs(classes) do
                    SETS_BY_CLASS[className] = SETS_BY_CLASS[className] or {}
                    table.insert(SETS_BY_CLASS[className], setID)
                end
            end
            
            imported = imported + 1
        end
    end
    
    ETB:Debug("Sets", string.format("✅ Imported %d Blizzard sets", imported))
    
    return imported
end

-- ============================================================================
-- SET TYPE DETECTION
-- ============================================================================

function Sets:DetermineSetType(setData)
    -- Analyze set metadata to determine type
    local name = setData.name or ""
    local desc = setData.description or ""
    local label = setData.label or ""
    local source = setData.source or ""
    local lower = name:lower()
    
    -- Check source first (most reliable)
    if source == "Unofficial" then
        -- Check for World Drop patterns
        local worldDropPatterns = {
            "glorious", "magnificent", "resplendent", "lofty", "vanguard", "exalted", 
            "jade", "saltstone", "tyrant", "hyperion", "warlord", "overlord", "commander",
            "bloodlust", "ornate", "wicked", "abjurer", "aboriginal", "ancestral", "apothecary",
            "barbaric", "beaded", "disciple", "native", "seer", "simple", "spellbinder"
        }
        for _, pattern in ipairs(worldDropPatterns) do
            if lower:match(pattern) then
                return "World"
            end
        end
        -- Check for crafted/profession
        if lower:match("crafted") or lower:match("profession") or 
           lower:match("blacksmith") or lower:match("tailor") or
           lower:match("leatherwork") or lower:match("engineer") then
            return "Crafted"
        end
        -- Default unofficial to World
        return "World"
    end
    
    -- Tier sets
    if name:match("Tier %d+") or name:match("T%d+") or 
       desc:match("Tier %d+") or label:match("Raid Finder") or
       label:match("Normal") or label:match("Heroic") or label:match("Mythic") then
        return SET_TYPES.TIER
    end
    
    -- PvP sets
    if name:match("Gladiator") or name:match("Season %d+") or
       name:match("Arena Season") or label:match("Elite") or
       desc:match("Arena") or desc:match("Honor") or lower:match("pvp") then
        return SET_TYPES.PVP
    end
    
    -- Challenge Mode sets (MoP only)
    if name:match("Challenge") or desc:match("Challenge Mode") then
        return SET_TYPES.CHALLENGE
    end
    
    -- Dungeon sets
    if name:match("Dungeon") or desc:match("dungeon") or lower:match("dungeon") then
        return SET_TYPES.DUNGEON
    end
    
    -- Crafted/Profession sets
    if lower:match("crafted") or lower:match("profession") or 
       lower:match("blacksmith") or lower:match("tailor") or
       lower:match("leatherwork") or lower:match("engineer") or
       desc:match("crafted") or desc:match("profession") then
        return "Crafted"
    end
    
    -- Default to tier if uncertain
    return SET_TYPES.TIER
end

-- ============================================================================
-- CLASS MASK UTILITIES
-- ============================================================================

function Sets:GetClassesFromMask(classMask)
    local classes = {}
    
    for className, mask in pairs(CLASS_MASK) do
        if bit.band(classMask, mask) > 0 then
            table.insert(classes, className)
        end
    end
    
    return classes
end

function Sets:GetClassNameFromMask(classMask)
    -- Get single class name (for single-class sets)
    return CLASS_NAMES[classMask] or "Unknown"
end

function Sets:IsClassAllowed(classMask, playerClass)
    local mask = CLASS_MASK[playerClass]
    if not mask then return false end
    
    return bit.band(classMask, mask) > 0
end

-- ============================================================================
-- SET ITEMS (LAZY LOADING)
-- ============================================================================

function Sets:GetSetItems(setID)
    -- Check if we already have items cached
    local setInfo = BLIZZARD_SETS[setID]
    if not setInfo then return {} end
    
    if setInfo.items and #setInfo.items > 0 then
        return setInfo.items
    end
    
    -- Lazy load items from Blizzard API
    if not API_AVAILABLE then return {} end
    
    local appearances = C_TransmogSets.GetSetPrimaryAppearances(setID)
    if not appearances then return {} end
    
    local items = {}
    
    for i, app in ipairs(appearances) do
        local itemData = {
            appearanceID = app.appearanceID,
            visualID = app.visualID,
            collected = app.collected or false,
            slot = self:GetSlotFromAppearance(app),
            
            -- Link to Database for additional info
            sources = {}  -- Will be populated on-demand from Database
        }
        
        table.insert(items, itemData)
        
        -- Index appearance -> sets mapping
        local appearanceID = app.appearanceID
        if appearanceID then
            APPEARANCE_TO_SETS[appearanceID] = APPEARANCE_TO_SETS[appearanceID] or {}
            table.insert(APPEARANCE_TO_SETS[appearanceID], setID)
        end
    end
    
    -- Cache for future use
    setInfo.items = items
    
    return items
end

function Sets:GetSlotFromAppearance(appearance)
    -- Attempt to determine slot from appearance data
    -- This is a heuristic - may need refinement
    if appearance.invType then
        return appearance.invType
    end
    
    return "Unknown"
end

-- ============================================================================
-- SET QUERIES
-- ============================================================================

function Sets:GetAllSets()
    local allSets = {}
    
    -- Combine Blizzard sets, custom sets, and unofficial sets
    for setID, setInfo in pairs(BLIZZARD_SETS) do
        table.insert(allSets, setInfo)
    end
    
    for _, customSet in ipairs(CUSTOM_SETS) do
        table.insert(allSets, customSet)
    end
    
    -- Add unofficial sets (via naming conventions)
    if ETB.UnofficialSets and ETB.UnofficialSets.GetSetsForIntegration then
        local unofficialSets = ETB.UnofficialSets:GetSetsForIntegration()
        for _, unofficialSet in ipairs(unofficialSets) do
            table.insert(allSets, unofficialSet)
        end
    end
    
    return allSets
end

function Sets:GetSetInfo(setID)
    -- Check Blizzard sets first
    if BLIZZARD_SETS[setID] then
        return BLIZZARD_SETS[setID]
    end
    
    -- Check custom sets
    for _, customSet in ipairs(CUSTOM_SETS) do
        if customSet.setID == setID then
            return customSet
        end
    end
    
    -- Check unofficial sets
    if ETB.UnofficialSets and ETB.UnofficialSets.GetSet then
        local unofficialSet = ETB.UnofficialSets:GetSet(setID)
        if unofficialSet then
            return unofficialSet
        end
    end
    
    return nil
end

function Sets:GetSetsByExpansion(expansionID)
    local setIDs = SETS_BY_EXPANSION[expansionID] or {}
    local sets = {}
    
    for _, setID in ipairs(setIDs) do
        local setInfo = self:GetSetInfo(setID)
        if setInfo then
            table.insert(sets, setInfo)
        end
    end
    
    return sets
end

function Sets:GetSetsByClass(className)
    local setIDs = SETS_BY_CLASS[className] or {}
    local sets = {}
    
    for _, setID in ipairs(setIDs) do
        local setInfo = self:GetSetInfo(setID)
        if setInfo then
            table.insert(sets, setInfo)
        end
    end
    
    return sets
end

function Sets:GetUsableSets()
    -- CRITICAL FIX: Query fresh from API to get current character's validForCharacter
    -- Cache contains values from initialization character
    local usableSets = {}
    
    if API_AVAILABLE then
        local allSets = C_TransmogSets.GetAllSets()
        
        for _, setData in ipairs(allSets) do
            if setData.validForCharacter then
                -- Get cached info for additional data, but use fresh validForCharacter
                local cachedSet = BLIZZARD_SETS[setData.setID]
                if cachedSet then
                    -- Create combined data with fresh validForCharacter
                    local setInfo = {}
                    for k, v in pairs(cachedSet) do
                        setInfo[k] = v
                    end
                    setInfo.validForCharacter = setData.validForCharacter
                    setInfo.collected = setData.collected
                    
                    table.insert(usableSets, setInfo)
                end
            end
        end
    else
        -- Fallback to cache if API not available
        for setID, setInfo in pairs(BLIZZARD_SETS) do
            if setInfo.validForCharacter then
                table.insert(usableSets, setInfo)
            end
        end
    end
    
    -- Sort by uiOrder (Blizzard's preferred display order)
    table.sort(usableSets, function(a, b)
        return a.uiOrder < b.uiOrder
    end)
    
    return usableSets
end

function Sets:GetCollectedSets()
    -- CRITICAL FIX: Query fresh from API to get current character's collected status
    local collectedSets = {}
    
    if API_AVAILABLE then
        local allSets = C_TransmogSets.GetAllSets()
        
        for _, setData in ipairs(allSets) do
            if setData.collected then
                -- Get cached info for additional data, but use fresh collected
                local cachedSet = BLIZZARD_SETS[setData.setID]
                if cachedSet then
                    -- Create combined data with fresh collected status
                    local setInfo = {}
                    for k, v in pairs(cachedSet) do
                        setInfo[k] = v
                    end
                    setInfo.validForCharacter = setData.validForCharacter
                    setInfo.collected = setData.collected
                    
                    table.insert(collectedSets, setInfo)
                end
            end
        end
    else
        -- Fallback to cache if API not available
        for setID, setInfo in pairs(BLIZZARD_SETS) do
            if setInfo.collected then
                table.insert(collectedSets, setInfo)
            end
        end
    end
    
    return collectedSets
end

-- ============================================================================
-- SET COMPLETION TRACKING
-- ============================================================================

function Sets:GetSetProgress(setID)
    -- CRITICAL: Only works with numeric setIDs (Blizzard sets)
    if type(setID) ~= "number" then
        -- This is an unofficial set or invalid setID, return 0/0
        return 0, 0, 0
    end
    
    -- CRITICAL FIX: Use GetAllSourceIDs to get ALL items in the set (most accurate)
    local total = 0
    local collected = 0
    
    if API_AVAILABLE and C_TransmogSets and C_TransmogSets.GetAllSourceIDs then
        local allSourceIDs = nil
        local success, result = pcall(C_TransmogSets.GetAllSourceIDs, setID)
        if success then
            allSourceIDs = result
        end
        
        if allSourceIDs and type(allSourceIDs) == "table" and #allSourceIDs > 0 then
            -- Group by appearanceID to count unique pieces
            local appearancesSeen = {}
            local appearanceCollected = {}
            
            for _, sourceID in ipairs(allSourceIDs) do
                if C_TransmogCollection and C_TransmogCollection.GetSourceInfo then
                    local sourceInfo = C_TransmogCollection.GetSourceInfo(sourceID)
                    if sourceInfo and sourceInfo.itemID and sourceInfo.appearanceID then
                        -- Filter Art Templates
                        if not sourceInfo.name or not (sourceInfo.name:match("^Art Template")) then
                            local appearanceID = sourceInfo.appearanceID
                            
                            -- Count unique appearances (one per slot)
                            if not appearancesSeen[appearanceID] then
                                appearancesSeen[appearanceID] = true
                                total = total + 1
                                
                                -- Check if this appearance is collected
                                local isCollected = false
                                
                                -- Method 1: Check CollectionTracker
                                if ETB.CollectionTracker and ETB.CollectionTracker.IsAppearanceCollected then
                                    isCollected = ETB.CollectionTracker:IsAppearanceCollected(appearanceID, true)
                                end
                                
                                -- Method 2: Check API flag
                                if not isCollected and sourceInfo.isCollected then
                                    isCollected = true
                                end
                                
                                -- Method 3: Check all variants
                                if not isCollected and C_TransmogCollection.GetAllAppearanceSources then
                                    local variantSourceIDs = C_TransmogCollection.GetAllAppearanceSources(appearanceID)
                                    if variantSourceIDs then
                                        for _, variantSourceID in ipairs(variantSourceIDs) do
                                            local variantInfo = C_TransmogCollection.GetSourceInfo(variantSourceID)
                                            if variantInfo and variantInfo.isCollected then
                                                isCollected = true
                                                break
                                            end
                                        end
                                    end
                                end
                                
                                if isCollected then
                                    collected = collected + 1
                                    appearanceCollected[appearanceID] = true
                                end
                            end
                        end
                    end
                end
            end
        end
    end
    
    -- Fallback to GetSetItems if GetAllSourceIDs fails
    if total == 0 then
        local items = self:GetSetItems(setID)
        total = #items
    
    for _, item in ipairs(items) do
            local isCollected = false
            
            if item.appearanceID then
                if ETB.CollectionTracker and ETB.CollectionTracker.IsAppearanceCollected then
                    isCollected = ETB.CollectionTracker:IsAppearanceCollected(item.appearanceID, true)
                end
                
                if not isCollected and C_TransmogCollection and C_TransmogCollection.GetAllAppearanceSources then
                    local sourceIDs = C_TransmogCollection.GetAllAppearanceSources(item.appearanceID)
                    if sourceIDs then
                        for _, sourceID in ipairs(sourceIDs) do
                            local sourceInfo = C_TransmogCollection.GetSourceInfo(sourceID)
                            if sourceInfo and sourceInfo.isCollected then
                                isCollected = true
                                break
                            end
                        end
                    end
                end
            end
            
            if not isCollected then
                isCollected = item.collected or false
            end
            
            if isCollected then
            collected = collected + 1
            end
        end
    end
    
    local percentage = total > 0 and (collected / total) * 100 or 0
    
    return collected, total, percentage
end

function Sets:IsSetComplete(setID)
    local collected, total = self:GetSetProgress(setID)
    return collected == total and total > 0
end

-- ============================================================================
-- PHASE 7: SET SOURCES & COMPLETION (Workflow Compliance)
-- ============================================================================

-- Get all sources for a set (Phase 7)
function Sets:GetSetSources(setID)
    if not setID or type(setID) ~= "number" then
        return {}
    end
    
    -- Try GetAllSourceIDs (ATT pattern)
    if API_AVAILABLE and C_TransmogSets and C_TransmogSets.GetAllSourceIDs then
        local success, sourceIDs = pcall(C_TransmogSets.GetAllSourceIDs, setID)
        if success and sourceIDs and type(sourceIDs) == "table" then
            local sources = {}
            for _, sourceID in ipairs(sourceIDs) do
                if sourceID then
                    -- Use TransmogAPI wrapper for bug workarounds
                    local sourceInfo = nil
                    if ETB.TransmogAPI and ETB.TransmogAPI.GetSourceInfo then
                        sourceInfo = ETB.TransmogAPI:GetSourceInfo(sourceID)
                    elseif C_TransmogCollection and C_TransmogCollection.GetSourceInfo then
                        local apiSuccess, apiResult = pcall(C_TransmogCollection.GetSourceInfo, sourceID)
                        if apiSuccess then
                            sourceInfo = apiResult
                        end
                    end
                    
                    if sourceInfo then
                        table.insert(sources, sourceInfo)
                    end
                end
            end
            return sources
        end
    end
    
    -- Fallback: Load from custom sets
    return self:GetCustomSetSources(setID) or {}
end

-- Get set completion (collected/total) (Phase 7)
function Sets:GetSetCompletion(setID)
    local sources = self:GetSetSources(setID)
    if not sources or #sources == 0 then
        return 0, 0
    end
    
    local total = 0
    local collected = 0
    
    for _, sourceInfo in ipairs(sources) do
        -- Use isValidSourceForPlayer (bug workaround)
        if sourceInfo.isValidSourceForPlayer or sourceInfo.canCollectWorkaround then
            total = total + 1
            if sourceInfo.isCollected then
                collected = collected + 1
            end
        end
    end
    
    return collected, total
end

-- Get custom set sources (fallback)
function Sets:GetCustomSetSources(setID)
    -- Check custom sets
    for _, customSet in ipairs(CUSTOM_SETS) do
        if customSet.setID == setID and customSet.items then
            local sources = {}
            for _, itemID in ipairs(customSet.items) do
                if itemID then
                    -- Get sourceID from itemID
                    if C_TransmogCollection and C_TransmogCollection.GetItemInfo then
                        local success, appearanceID, sourceID = pcall(C_TransmogCollection.GetItemInfo, itemID)
                        if success and sourceID then
                            local sourceInfo = nil
                            if ETB.TransmogAPI and ETB.TransmogAPI.GetSourceInfo then
                                sourceInfo = ETB.TransmogAPI:GetSourceInfo(sourceID)
                            elseif C_TransmogCollection and C_TransmogCollection.GetSourceInfo then
                                local apiSuccess, apiResult = pcall(C_TransmogCollection.GetSourceInfo, sourceID)
                                if apiSuccess then
                                    sourceInfo = apiResult
                                end
                            end
                            
                            if sourceInfo then
                                table.insert(sources, sourceInfo)
                            end
                        end
                    end
                end
            end
            return sources
        end
    end
    
    return {}
end

-- ============================================================================
-- FAVORITES
-- ============================================================================

function Sets:IsFavorite(setID)
    local setInfo = self:GetSetInfo(setID)
    return setInfo and setInfo.favorite or false
end

function Sets:SetFavorite(setID, isFavorite)
    -- Update Blizzard's favorite status
    if API_AVAILABLE and C_TransmogSets.SetIsFavorite then
        C_TransmogSets.SetIsFavorite(setID, isFavorite)
    end
    
    -- Update local cache
    local setInfo = self:GetSetInfo(setID)
    if setInfo then
        setInfo.favorite = isFavorite
    end
end

-- ============================================================================
-- CUSTOM SETS (USER-CREATED)
-- ============================================================================

function Sets:LoadCustomSets()
    if not ETB_CustomSets then
        ETB_CustomSets = {}
    end
    
    CUSTOM_SETS = ETB_CustomSets
    
    ETB:Debug("Sets", string.format("Loaded %d custom sets", #CUSTOM_SETS))
end

function Sets:SaveCustomSets()
    ETB_CustomSets = CUSTOM_SETS
end

function Sets:CreateCustomSet(name, appearances)
    local customSet = {
        setID = "custom_" .. time(),  -- Unique ID
        name = name,
        description = "Custom set created by player",
        source = "Custom",
        type = SET_TYPES.CUSTOM,
        validForCharacter = true,
        collected = false,
        favorite = false,
        expansionID = 4,  -- MoP
        
        items = appearances or {}
    }
    
    table.insert(CUSTOM_SETS, customSet)
    self:SaveCustomSets()
    
    ETB:Debug("Sets", string.format("Created custom set: %s", name))
    
    return customSet
end

function Sets:DeleteCustomSet(setID)
    for i, customSet in ipairs(CUSTOM_SETS) do
        if customSet.setID == setID then
            table.remove(CUSTOM_SETS, i)
            self:SaveCustomSets()
            ETB:Debug("Sets", string.format("Deleted custom set: %s", customSet.name))
            return true
        end
    end
    
    return false
end

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

function Sets:GetStatistics()
    local stats = {
        totalSets = 0,
        blizzardSets = 0,
        customSets = #CUSTOM_SETS,
        usableSets = 0,
        collectedSets = 0,
        
        byExpansion = {},
        byClass = {},
        byType = {}
    }
    
    -- CRITICAL FIX: Query fresh from API to get current character's validForCharacter
    -- Don't use cache as it contains values from initialization character
    if API_AVAILABLE then
        local allSets = C_TransmogSets.GetAllSets()
        
        for _, setData in ipairs(allSets) do
            stats.totalSets = stats.totalSets + 1
            stats.blizzardSets = stats.blizzardSets + 1
            
            -- Use FRESH validForCharacter value for current character
            if setData.validForCharacter then
                stats.usableSets = stats.usableSets + 1
            end
            
            -- Use FRESH collected value
            if setData.collected then
                stats.collectedSets = stats.collectedSets + 1
            end
            
            -- By expansion
            local expID = setData.expansionID or 0
            stats.byExpansion[expID] = (stats.byExpansion[expID] or 0) + 1
            
            -- By type (use cached for this as it doesn't change per character)
            local cachedSet = BLIZZARD_SETS[setData.setID]
            if cachedSet then
                local setType = cachedSet.type or "Unknown"
                stats.byType[setType] = (stats.byType[setType] or 0) + 1
            end
        end
    else
        -- Fallback to cache if API not available
        for setID, setInfo in pairs(BLIZZARD_SETS) do
            stats.totalSets = stats.totalSets + 1
            stats.blizzardSets = stats.blizzardSets + 1
            
            if setInfo.validForCharacter then
                stats.usableSets = stats.usableSets + 1
            end
            
            if setInfo.collected then
                stats.collectedSets = stats.collectedSets + 1
            end
            
            local expID = setInfo.expansionID or 0
            stats.byExpansion[expID] = (stats.byExpansion[expID] or 0) + 1
            
            local setType = setInfo.type or "Unknown"
            stats.byType[setType] = (stats.byType[setType] or 0) + 1
        end
    end
    
    stats.totalSets = stats.totalSets + stats.customSets
    
    return stats
end

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

function Sets:Initialize()
    if INITIALIZED then return end
    
    ETB:Debug("Sets", "Initializing Sets v3.0 (API-Based)...")
    
    -- Check API availability
    self:CheckAPIAvailability()
    
    -- Initialize Unofficial Sets system
    if ETB.UnofficialSets and ETB.UnofficialSets.Initialize then
        ETB.UnofficialSets:Initialize()
    end
    
    -- Load custom sets from SavedVariables
    self:LoadCustomSets()
    
    -- Import Blizzard sets (auto-load if API available)
    if API_AVAILABLE then
        local count = self:ImportBlizzardSets()
        
        if count > 0 then
            ETB:Debug("Sets", string.format("✅ Sets v3.0 initialized: %d sets available", count))
        else
            ETB:Debug("Sets", "⚠️ Sets v3.0 initialized: No Blizzard sets found")
        end
    else
        ETB:Debug("Sets", "⚠️ Sets v3.0 initialized: API not available")
    end
    
    INITIALIZED = true
    Sets.INITIALIZED = true  -- Expose flag to MainFrame (ETB.Sets.INITIALIZED)
    Sets.initialized = true  -- Also expose lowercase for compatibility
end

-- ============================================================================
-- REFRESH
-- ============================================================================

function Sets:Refresh()
    -- FIXED: Throttle refresh to prevent running on every single loot
    -- Only refresh once every 30 seconds maximum
    local now = GetTime()
    if self.lastRefreshTime and (now - self.lastRefreshTime) < 30 then
        ETB:Debug("Sets", "Refresh throttled (too soon)")
        return
    end
    self.lastRefreshTime = now
    
    -- Re-import Blizzard sets (to pick up new collections)
    if API_AVAILABLE then
        self:ImportBlizzardSets()
        ETB:Debug("Sets", "Sets refreshed from Blizzard API")
    end
end

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

SLASH_SETSV3_1 = "/setsv3"
SlashCmdList["SETSV3"] = function(msg)
    local command, arg1 = strsplit(" ", msg)
    command = command and command:lower() or ""
    
    if command == "stats" or command == "" then
        -- Show statistics
        local stats = Sets:GetStatistics()
        
        print("|cff00ff00=== ETB Sets v3.0 Statistics ===|r")
        print(string.format("Total Sets: |cffffffff%d|r", stats.totalSets))
        print(string.format("  Blizzard Sets: |cff00ff00%d|r", stats.blizzardSets))
        print(string.format("  Custom Sets: |cff00ffff%d|r", stats.customSets))
        print(string.format("Usable by You: |cffffff00%d|r", stats.usableSets))
        print(string.format("Collected: |cff00ff00%d|r (%.1f%%)", 
            stats.collectedSets, (stats.collectedSets / stats.totalSets) * 100))
        print("")
        print("|cff00ff00By Expansion:|r")
        for expID = 0, 4 do
            local count = stats.byExpansion[expID] or 0
            if count > 0 then
                print(string.format("  %s: |cffffffff%d|r", EXPANSION_NAMES[expID], count))
            end
        end
        print("")
        print("|cff00ff00By Type:|r")
        for setType, count in pairs(stats.byType) do
            print(string.format("  %s: |cffffffff%d|r", setType, count))
        end
        
    elseif command == "refresh" then
        -- Refresh sets from API
        Sets:Refresh()
        print("|cff00ff00[ETB Sets]|r Sets refreshed from Blizzard API")
        
    elseif command == "info" and arg1 then
        -- Show info for specific set
        local setID = tonumber(arg1)
        if setID then
            local setInfo = Sets:GetSetInfo(setID)
            if setInfo then
                print("|cff00ff00=== Set Info ===|r")
                print(string.format("Name: |cffffffff%s|r", setInfo.name))
                print(string.format("ID: |cffffffff%d|r", setInfo.setID))
                print(string.format("Type: |cffffffff%s|r", setInfo.type))
                print(string.format("Expansion: |cffffffff%s|r", EXPANSION_NAMES[setInfo.expansionID or 0]))
                print(string.format("Usable: |cffffffff%s|r", setInfo.validForCharacter and "Yes" or "No"))
                print(string.format("Collected: |cffffffff%s|r", setInfo.collected and "Yes" or "No"))
                
                local collected, total, pct = Sets:GetSetProgress(setID)
                print(string.format("Progress: |cffffffff%d/%d|r (%.1f%%)", collected, total, pct))
            else
                print("|cffff0000[ETB Sets]|r Set not found: " .. setID)
            end
        else
            print("|cffff0000[ETB Sets]|r Usage: /setsv3 info <setID>")
        end
        
    elseif command == "help" then
        print("|cff00ff00=== ETB Sets v3.0 Commands ===|r")
        print("|cffffffff/setsv3|r - Show statistics")
        print("|cffffffff/setsv3 stats|r - Show detailed statistics")
        print("|cffffffff/setsv3 refresh|r - Refresh sets from API")
        print("|cffffffff/setsv3 info <setID>|r - Show info for specific set")
        print("|cffffffff/setsv3 help|r - Show this help")
    else
        print("|cffff0000[ETB Sets]|r Unknown command. Type '/setsv3 help' for usage.")
    end
end

-- ============================================================================
-- EVENTS
-- ============================================================================

-- Refresh sets when transmog collection changes
local frame = CreateFrame("Frame")
frame:RegisterEvent("TRANSMOG_COLLECTION_UPDATED")
frame:SetScript("OnEvent", function(self, event)
    if event == "TRANSMOG_COLLECTION_UPDATED" then
        Sets:Refresh()
    end
end)

-- ============================================================================
-- INITIALIZATION TRIGGER
-- ============================================================================

-- Auto-initialize when addon loads
if ETB and ETB.RegisterCallback then
    ETB:RegisterCallback("OnDatabaseReady", function()
        Sets:Initialize()
    end)
else
    -- Fallback: initialize after a short delay
    C_Timer.After(2, function()
        Sets:Initialize()
    end)
end

-- ============================================================================
-- MODULE READY
-- ============================================================================

print("|cff00ff00[ETB]|r Sets v3.0 loaded - Type |cffffffff/setsv3|r for statistics")