--[[
============================================================================
ETB v11.0 - CustomSets.lua
============================================================================
Custom/Unofficial Transmog Sets - Sets that share naming conventions or 
visual themes but aren't in Blizzard's C_TransmogSets API.

Examples:
- Druid of the Fang (Wailing Caverns)
- Defias Leather (Deadmines)
- Embrace of the Viper (Wailing Caverns)
- Scarlet Monastery sets

This module provides:
1. Pre-defined unofficial sets from Classic through MoP
2. Discovery tools to find potential custom sets
3. User-definable sets stored in saved variables
============================================================================
]]--

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

ETB.CustomSets = {
    -- Pre-defined sets (static)
    predefinedSets = {},
    
    -- User-added sets (saved to ETB_CustomSetsData)
    userSets = {},
    
    -- Combined active sets
    allCustomSets = {},
    
    -- Discovery cache
    discoveryCache = {},
    
    initialized = false,
}

local CS = ETB.CustomSets

-- =============================================================================
-- SLOT CONSTANTS
-- =============================================================================

local SLOTS = {
    HEAD = 1,
    SHOULDER = 2,
    CHEST = 3,
    WAIST = 4,
    LEGS = 5,
    FEET = 6,
    WRIST = 7,
    HANDS = 8,
    BACK = 9,  -- For cloaks that match sets
}

local SLOT_NAMES = {
    [1] = "Head",
    [2] = "Shoulders",
    [3] = "Chest",
    [4] = "Waist",
    [5] = "Legs",
    [6] = "Feet",
    [7] = "Wrists",
    [8] = "Hands",
    [9] = "Back",
}

-- =============================================================================
-- PRE-DEFINED UNOFFICIAL SETS
-- Format: { name, armorType, items = {[SLOT] = itemID, ...}, source, expansion, faction }
-- =============================================================================

CS.predefinedSets = {
    -- =========================================================================
    -- CLASSIC DUNGEON SETS (Verified)
    -- =========================================================================
    
    -- Wailing Caverns - Druid of the Fang (Leather)
    {
        setID = "CUSTOM_DRUID_FANG",
        name = "Druid of the Fang",
        armorType = "LEATHER",
        source = "Wailing Caverns",
        expansion = 0, -- Classic
        faction = nil, -- Both
        items = {
            [SLOTS.CHEST] = 6473,     -- Armor of the Fang
            [SLOTS.WAIST] = 10412,    -- Belt of the Fang
            [SLOTS.LEGS] = 10410,     -- Leggings of the Fang
            [SLOTS.FEET] = 10411,     -- Footpads of the Fang
            [SLOTS.HANDS] = 10413,    -- Gloves of the Fang
        },
    },
    
    -- Classic World Drop - Emerald Set (Plate)
    {
        setID = "CUSTOM_EMERALD_SET",
        name = "Emerald Set",
        armorType = "PLATE",
        source = "World Drop",
        expansion = 0, -- Classic
        faction = nil,
        items = {
            [SLOTS.HEAD] = 10279,     -- Emerald Helm
            [SLOTS.SHOULDER] = 10281, -- Emerald Pauldrons
            [SLOTS.CHEST] = 10275,    -- Emerald Breastplate
            [SLOTS.WAIST] = 10278,    -- Emerald Girdle
            [SLOTS.LEGS] = 10280,     -- Emerald Legplates
            [SLOTS.FEET] = 10276,     -- Emerald Sabatons
            [SLOTS.WRIST] = 10282,    -- Emerald Vambraces
            [SLOTS.HANDS] = 10277,    -- Emerald Gauntlets
        },
    },
}

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

function CS:Initialize()
    if self.initialized then return end
    
    -- Load user-defined sets from saved variables
    if ETB_CustomSetsData then
        self.userSets = ETB_CustomSetsData.sets or {}
    end
    
    -- Combine predefined and user sets
    self:RebuildAllSets()
    
    self.initialized = true
    ETB:Debug("CustomSets", string.format("Initialized with %d predefined + %d user sets", 
        #self.predefinedSets, self:CountTable(self.userSets)))
end

function CS:RebuildAllSets()
    wipe(self.allCustomSets)
    
    -- Add predefined sets
    for _, set in ipairs(self.predefinedSets) do
        if set.setID then
            self.allCustomSets[set.setID] = set
        end
    end
    
    -- Add user sets (can override predefined)
    for setID, set in pairs(self.userSets) do
        self.allCustomSets[setID] = set
    end
end

-- =============================================================================
-- SET ACCESS
-- =============================================================================

function CS:GetAllSets()
    return self.allCustomSets
end

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

function CS:GetSetsByExpansion(expansionID)
    local sets = {}
    for setID, set in pairs(self.allCustomSets) do
        if set.expansion == expansionID then
            sets[setID] = set
        end
    end
    return sets
end

function CS:GetSetsByArmorType(armorType)
    local sets = {}
    for setID, set in pairs(self.allCustomSets) do
        if set.armorType == armorType then
            sets[setID] = set
        end
    end
    return sets
end

function CS:GetSetsForPlayer()
    local primaryArmor = ETB.BlizzardData and ETB.BlizzardData:GetPlayerPrimaryArmor() or "CLOTH"
    local faction = UnitFactionGroup("player")
    
    local sets = {}
    for setID, set in pairs(self.allCustomSets) do
        local canUse = true
        
        -- Check armor type
        if set.armorType and set.armorType ~= primaryArmor then
            canUse = false
        end
        
        -- Check faction
        if set.faction and set.faction ~= faction then
            canUse = false
        end
        
        if canUse then
            sets[setID] = set
        end
    end
    return sets
end

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

function CS:GetSetCompletion(setID)
    local set = self.allCustomSets[setID]
    if not set or not set.items then
        return 0, 0
    end
    
    local collected = 0
    local total = 0
    
    for slot, itemID in pairs(set.items) do
        if itemID and type(itemID) == "number" then
            total = total + 1
            
            -- Check if collected via appearance source chain
            -- PlayerHasTransmog expects sourceID, not itemID
            if C_TransmogCollection and C_TransmogCollection.GetItemInfo then
                local ok, appearanceID = pcall(C_TransmogCollection.GetItemInfo, itemID)
                if ok and appearanceID and C_TransmogCollection.GetAppearanceSources then
                    local srcOK, sources = pcall(C_TransmogCollection.GetAppearanceSources, appearanceID)
                    if srcOK 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
                                    collected = collected + 1
                                    break
                                end
                            end
                        end
                    end
                end
            end
        end
    end
    
    return collected, total
end

function CS:GetSetItems(setID)
    local set = self.allCustomSets[setID]
    if not set or not set.items then return {} end
    
    local items = {}
    for slot, itemID in pairs(set.items) do
        if itemID and type(itemID) == "number" then
            local name, link, quality, _, _, _, _, _, _, icon = GetItemInfo(itemID)
            local isCollected = false
            -- Use sourceID chain, not PlayerHasTransmog(itemID)
            if C_TransmogCollection and C_TransmogCollection.GetItemInfo then
                local ok, appearanceID = pcall(C_TransmogCollection.GetItemInfo, itemID)
                if ok and appearanceID and C_TransmogCollection.GetAppearanceSources then
                    local srcOK, sources = pcall(C_TransmogCollection.GetAppearanceSources, appearanceID)
                    if srcOK 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
                                    isCollected = true
                                    break
                                end
                            end
                        end
                    end
                end
            end
            
            items[#items + 1] = {
                slot = slot,
                slotName = SLOT_NAMES[slot] or "Unknown",
                itemID = itemID,
                name = name or "Loading...",
                icon = icon,
                quality = tonumber(quality) or 2,
                isCollected = isCollected,
            }
        end
    end
    
    -- Sort by slot
    table.sort(items, function(a, b) return a.slot < b.slot end)
    
    return items
end

-- =============================================================================
-- DISCOVERY SYSTEM - Find potential custom sets from loaded items
-- =============================================================================

function CS:DiscoverPotentialSets(items, minPieces)
    minPieces = minPieces or 3
    
    -- Group items by name prefix
    local prefixGroups = {}
    
    for _, item in ipairs(items) do
        if item.name and item.name ~= "Loading..." then
            -- Extract potential set prefixes
            local prefixes = self:ExtractSetPrefixes(item.name)
            
            for _, prefix in ipairs(prefixes) do
                if not prefixGroups[prefix] then
                    prefixGroups[prefix] = {
                        prefix = prefix,
                        items = {},
                        slots = {},
                        armorType = nil,
                    }
                end
                
                -- Add item if not duplicate slot
                local slotKey = item.invType or "unknown"
                if not prefixGroups[prefix].slots[slotKey] then
                    prefixGroups[prefix].slots[slotKey] = true
                    table.insert(prefixGroups[prefix].items, item)
                    
                    -- Track armor type
                    if item.armorType and not prefixGroups[prefix].armorType then
                        prefixGroups[prefix].armorType = item.armorType
                    end
                end
            end
        end
    end
    
    -- Filter to groups with minimum pieces and not already a known set
    local potentialSets = {}
    for prefix, group in pairs(prefixGroups) do
        if #group.items >= minPieces then
            -- Check if this matches a known custom set
            local isKnown = false
            for setID, set in pairs(self.allCustomSets) do
                if set.name and set.name:lower():find(prefix:lower(), 1, true) then
                    isKnown = true
                    break
                end
            end
            
            if not isKnown then
                table.insert(potentialSets, {
                    name = prefix,
                    pieceCount = #group.items,
                    armorType = group.armorType,
                    items = group.items,
                })
            end
        end
    end
    
    -- Sort by piece count (most pieces first)
    table.sort(potentialSets, function(a, b) return a.pieceCount > b.pieceCount end)
    
    return potentialSets
end

function CS:ExtractSetPrefixes(itemName)
    local prefixes = {}
    
    -- Common patterns for set items:
    -- "X of the Y" (e.g., "Armor of the Fang")
    -- "X's Y" (e.g., "Herod's Shoulder")
    -- "Adjective X" (e.g., "Scarlet Boots")
    
    -- Pattern: "Word of the Word"
    local ofThe = itemName:match("^(.+ of the %w+)")
    if ofThe then
        table.insert(prefixes, ofThe)
    end
    
    -- Pattern: "Word's" 
    local possessive = itemName:match("^(%w+'s)")
    if possessive then
        table.insert(prefixes, possessive)
    end
    
    -- Pattern: First two words (common for dungeon sets)
    local firstTwo = itemName:match("^(%w+ %w+)")
    if firstTwo and #firstTwo > 5 then
        -- Exclude generic prefixes
        local generic = {
            ["of the"] = true, ["the "] = true,
            ["a "] = true, ["an "] = true,
        }
        if not generic[firstTwo:lower()] then
            table.insert(prefixes, firstTwo)
        end
    end
    
    -- Pattern: Single significant word (for simple sets like "Defias")
    local firstWord = itemName:match("^(%w+)")
    if firstWord and #firstWord >= 5 then
        -- Exclude generic adjectives
        local adjectives = {
            ["heavy"] = true, ["light"] = true, ["thick"] = true,
            ["rough"] = true, ["fine"] = true, ["crude"] = true,
            ["lesser"] = true, ["greater"] = true, ["superior"] = true,
        }
        if not adjectives[firstWord:lower()] then
            table.insert(prefixes, firstWord)
        end
    end
    
    return prefixes
end

-- =============================================================================
-- USER SET MANAGEMENT
-- =============================================================================

function CS:AddUserSet(setID, setData)
    if not setID or not setData then return false end
    
    -- Validate
    if not setData.name then
        ETB:Print("Error: Custom set must have a name")
        return false
    end
    
    setData.setID = setID
    setData.isUserDefined = true
    
    self.userSets[setID] = setData
    self:RebuildAllSets()
    self:SaveUserSets()
    
    ETB:Print(string.format("Added custom set: %s", setData.name))
    return true
end

-- =============================================================================
-- IMPORT FROM SET BUILDER
-- =============================================================================

function CS:ImportFromSetBuilder(setBuilderData)
    if ETB.debug then print("|cff00ffff[ETB Import Debug]|r === Starting Import ===") end
    
    if not setBuilderData then
        if ETB.debug then print("|cffff0000[ETB Import]|r No set data provided") end
        return false
    end
    
    -- Debug: dump incoming data
    if ETB.debug then print("|cff00ffff[ETB Import Debug]|r setBuilderData type: " .. type(setBuilderData)) end
    if ETB.debug then print("|cff00ffff[ETB Import Debug]|r name: " .. tostring(setBuilderData.name)) end
    if ETB.debug then print("|cff00ffff[ETB Import Debug]|r armorType: " .. tostring(setBuilderData.armorType)) end
    if ETB.debug then print("|cff00ffff[ETB Import Debug]|r source: " .. tostring(setBuilderData.source)) end
    
    local setName = setBuilderData.name
    if not setName or setName == "" or setName == "Untitled Set" or setName == "Unnamed Set" then
        if ETB.debug then print("|cffff0000[ETB Import]|r Please name your set before importing") end
        return false
    end
    
    -- Debug items table
    if setBuilderData.items then
        if ETB.debug then print("|cff00ffff[ETB Import Debug]|r items table exists, iterating...") end
        for k, v in pairs(setBuilderData.items) do
            if ETB.debug then print(string.format("|cff00ffff[ETB Import Debug]|r   Key: %s (type: %s), Value type: %s", tostring(k), type(k), type(v))) end


            if type(v) == "table" then
                if ETB.debug then print(string.format("|cff00ffff[ETB Import Debug]|r     itemID in table: %s", tostring(v.itemID))) end
            end
        end
    else
        if ETB.debug then print("|cffff0000[ETB Import Debug]|r items table is nil!") end
    end
    
    -- Count items in the set
    local itemCount = 0
    local items = {}
    
    for slot, itemData in pairs(setBuilderData.items or {}) do
        -- Handle both formats: itemData can be {itemID=x} or just x
        local itemID = nil
        if type(itemData) == "table" and itemData.itemID then
            itemID = itemData.itemID
        elseif type(itemData) == "number" then
            itemID = itemData
        end
        
        if itemID then
            -- Ensure slot is a number
            local slotNum = tonumber(slot)
            if slotNum then
                items[slotNum] = itemID
                itemCount = itemCount + 1
                if ETB.debug then print(string.format("|cff00ff00[ETB Import]|r Slot %d = ItemID %d", slotNum, itemID)) end
            else
                if ETB.debug then print(string.format("|cffff0000[ETB Import Debug]|r Could not convert slot to number: %s", tostring(slot))) end
            end
        else
            if ETB.debug then print(string.format("|cffff0000[ETB Import Debug]|r No itemID found for slot %s", tostring(slot))) end
        end
    end
    
    if ETB.debug then print(string.format("|cff00ffff[ETB Import Debug]|r Total items extracted: %d", itemCount)) end
    
    if itemCount == 0 then
        if ETB.debug then print("|cffff0000[ETB Import]|r Set has no items") end
        return false
    end
    
    -- Generate unique setID
    local setID = "SB_" .. setName:upper():gsub("%s+", "_"):gsub("[^%w_]", "") .. "_" .. time()
    if ETB.debug then print("|cff00ffff[ETB Import Debug]|r Generated setID: " .. setID) end
    
    -- Build set data in ETB format
    local setData = {
        setID = setID,
        name = setName,
        armorType = setBuilderData.armorType or "UNKNOWN",
        source = setBuilderData.source or "Set Builder",
        expansion = setBuilderData.expansion or 0,
        faction = setBuilderData.faction,
        items = items,
        isUserDefined = true,
        importedFrom = "SetBuilder",
        importDate = date("%Y-%m-%d %H:%M"),
    }
    
    -- Debug: verify items in setData
    if ETB.debug then print("|cff00ffff[ETB Import Debug]|r setData.items contents:") end
    for slot, id in pairs(setData.items) do
        if ETB.debug then print(string.format("|cff00ffff[ETB Import Debug]|r   [%d] = %d", slot, id)) end
    end
    
    -- Add to user sets
    self.userSets[setID] = setData
    if ETB.debug then print("|cff00ffff[ETB Import Debug]|r Added to userSets") end
    
    -- Rebuild and save
    self:RebuildAllSets()
    if ETB.debug then print("|cff00ffff[ETB Import Debug]|r RebuildAllSets completed") end
    
    -- Verify it's in allCustomSets
    local verify = self.allCustomSets[setID]
    if verify then
        if ETB.debug then print("|cff00ff00[ETB Import Debug]|r Verified in allCustomSets: " .. verify.name) end
        if verify.items then
            local count = 0
            for _ in pairs(verify.items) do count = count + 1 end
            if ETB.debug then print("|cff00ff00[ETB Import Debug]|r   Items count in allCustomSets: " .. count) end
        else
            if ETB.debug then print("|cffff0000[ETB Import Debug]|r   Items is nil in allCustomSets!") end
        end
    else
        if ETB.debug then print("|cffff0000[ETB Import Debug]|r NOT FOUND in allCustomSets!") end
    end
    
    self:SaveUserSets()
    
    if ETB.debug then print(string.format("|cff00ff00[ETB Import]|r Successfully imported: %s (%d pieces)", setName, itemCount)) end
    if ETB.debug then print("|cff00ffff[ETB Import Debug]|r === Import Complete ===") end
    
    return true, setID
end

function CS:RemoveUserSet(setID)
    if not self.userSets[setID] then
        ETB:Print("Set not found or is predefined (cannot remove)")
        return false
    end
    
    local name = self.userSets[setID].name
    self.userSets[setID] = nil
    self:RebuildAllSets()
    self:SaveUserSets()
    
    ETB:Print(string.format("Removed custom set: %s", name))
    return true
end

function CS:SaveUserSets()
    ETB_CustomSetsData = {
        sets = self.userSets,
        version = 1,
    }
end

-- =============================================================================
-- CONVERT DISCOVERED SET TO USER SET
-- =============================================================================

function CS:ConvertDiscoveredToSet(discoveredSet)
    if not discoveredSet or not discoveredSet.name then return nil end
    
    -- Build items table from discovered items
    local items = {}
    for _, item in ipairs(discoveredSet.items or {}) do
        local slot = self:InvTypeToSlot(item.invType)
        if slot and item.itemID then
            items[slot] = item.itemID
        end
    end
    
    -- Generate unique ID
    local setID = "USER_" .. discoveredSet.name:upper():gsub("%s+", "_"):gsub("[^%w_]", "")
    
    local setData = {
        name = discoveredSet.name,
        armorType = discoveredSet.armorType,
        source = "Discovered",
        expansion = nil, -- Unknown
        faction = nil,
        items = items,
        isUserDefined = true,
    }
    
    return setID, setData
end

function CS:InvTypeToSlot(invType)
    local mapping = {
        INVTYPE_HEAD = SLOTS.HEAD,
        INVTYPE_SHOULDER = SLOTS.SHOULDER,
        INVTYPE_CHEST = SLOTS.CHEST,
        INVTYPE_ROBE = SLOTS.CHEST,
        INVTYPE_WAIST = SLOTS.WAIST,
        INVTYPE_LEGS = SLOTS.LEGS,
        INVTYPE_FEET = SLOTS.FEET,
        INVTYPE_WRIST = SLOTS.WRIST,
        INVTYPE_HAND = SLOTS.HANDS,
        INVTYPE_CLOAK = SLOTS.BACK,
    }
    return mapping[invType]
end

-- =============================================================================
-- IMPORT FROM SET BUILDER
-- =============================================================================

function CS:ImportFromSetBuilder(setData)
    if not setData then
        ETB:Print("|cffff0000Import failed: No set data provided|r")
        return false
    end
    
    if not setData.name or setData.name == "" then
        ETB:Print("|cffff0000Import failed: Set must have a name|r")
        return false
    end
    
    -- Count items and extract itemIDs
    local itemCount = 0
    local extractedItems = {}
    
    if setData.items then
        for slot, itemData in pairs(setData.items) do
            local slotNum = tonumber(slot)
            if slotNum then
                -- Handle both formats:
                -- 1. Set Builder format: {itemID = x, name = y, icon = z}
                -- 2. Direct format: itemID (number)
                local itemID = nil
                if type(itemData) == "table" and itemData.itemID then
                    itemID = itemData.itemID
                elseif type(itemData) == "number" then
                    itemID = itemData
                end
                
                if itemID then
                    extractedItems[slotNum] = itemID
                    itemCount = itemCount + 1
                end
            end
        end
    end
    
    if itemCount == 0 then
        ETB:Print("|cffff0000Import failed: Set has no items|r")
        return false
    end
    
    -- Generate unique ID
    local setID = "SB_" .. setData.name:upper():gsub("%s+", "_"):gsub("[^%w_]", "") .. "_" .. time()
    
    -- Build the set data
    local newSet = {
        setID = setID,
        name = setData.name,
        armorType = setData.armorType or "UNKNOWN",
        source = setData.source or "Set Builder",
        expansion = setData.expansion or 0,
        faction = setData.faction,
        items = extractedItems,
        isUserDefined = true,
        importedFrom = "SetBuilder",
        importDate = date("%Y-%m-%d %H:%M"),
    }
    
    -- Add to user sets
    self.userSets[setID] = newSet
    self:RebuildAllSets()
    self:SaveUserSets()
    
    ETB:Print(string.format("|cff00ff00Imported set:|r %s (%d pieces)", setData.name, itemCount))
    ETB:Print("|cff888888View in Sets tab of ETB browser|r")
    
    return true, setID
end

-- =============================================================================
-- UTILITIES
-- =============================================================================

function CS:CountTable(t)
    local count = 0
    for _ in pairs(t) do count = count + 1 end
    return count
end

function CS:PrintStats()
    ETB:Print("=== CUSTOM SETS ===")
    ETB:Print(string.format("Predefined: %d sets", #self.predefinedSets))
    ETB:Print(string.format("User-defined: %d sets", self:CountTable(self.userSets)))
    ETB:Print(string.format("Total: %d sets", self:CountTable(self.allCustomSets)))
end

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

ETB.EventBus:Register("ETB_INITIALIZED", function()
    CS:Initialize()
end, ETB.EventBus.Priority.NORMAL)

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