--[[
============================================================================
ETB ColorSearch — Multi-color filtering via pre-built ColorMap
============================================================================
19,697 MoP visualIDs mapped to 1-3 color tags (88% coverage).
Sources: WardrobeSort FileData (filename keywords) + RGB data (via ItemAppearance bridge).
Items with multiple colors match ANY selected filter color.

Encoding: single=idx, dual=p*100+s, triple=p*10000+s*100+t
Index: 1=RED 2=ORANGE 3=YELLOW 4=GREEN 5=TEAL 6=BLUE 7=PURPLE 8=PINK 9=WHITE 10=BLACK 11=BROWN 12=GRAY
============================================================================
]]--

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

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

CS.Categories = {
    { id = "RED",    idx = 1,  name = "Red",    swatch = {0.80, 0.15, 0.15} },
    { id = "ORANGE", idx = 2,  name = "Orange", swatch = {0.86, 0.50, 0.12} },
    { id = "YELLOW", idx = 3,  name = "Yellow", swatch = {0.86, 0.78, 0.20} },
    { id = "GREEN",  idx = 4,  name = "Green",  swatch = {0.20, 0.70, 0.20} },
    { id = "TEAL",   idx = 5,  name = "Teal",   swatch = {0.15, 0.67, 0.67} },
    { id = "BLUE",   idx = 6,  name = "Blue",   swatch = {0.20, 0.30, 0.82} },
    { id = "PURPLE", idx = 7,  name = "Purple", swatch = {0.55, 0.20, 0.78} },
    { id = "PINK",   idx = 8,  name = "Pink",   swatch = {0.86, 0.40, 0.60} },
    { id = "WHITE",  idx = 9,  name = "White",  swatch = {0.90, 0.90, 0.90} },
    { id = "BLACK",  idx = 10, name = "Black",  swatch = {0.12, 0.12, 0.12} },
    { id = "BROWN",  idx = 11, name = "Brown",  swatch = {0.55, 0.35, 0.18} },
    { id = "GRAY",   idx = 12, name = "Gray",   swatch = {0.50, 0.50, 0.50} },
}

CS.CategoryByID = {}
CS.CategoryByIdx = {}
for _, cat in ipairs(CS.Categories) do
    CS.CategoryByID[cat.id] = cat
    CS.CategoryByIdx[cat.idx] = cat
end

-- Decode packed color value into 1-3 color indices
-- NOTE: Creates a new table — use PackedHasColor for filter loops to avoid allocation
local function DecodeColors(packed)
    if not packed or packed <= 0 then return nil end
    if packed <= 12 then
        return { packed }
    elseif packed <= 1299 then
        local primary = math.floor(packed / 100)
        local secondary = packed % 100
        if secondary > 0 and secondary <= 12 then
            return { primary, secondary }
        end
        return { primary }
    else
        local primary = math.floor(packed / 10000)
        local remainder = packed % 10000
        local secondary = math.floor(remainder / 100)
        local tertiary = remainder % 100
        local result = { primary }
        if secondary > 0 and secondary <= 12 then result[2] = secondary end
        if tertiary > 0 and tertiary <= 12 then result[#result + 1] = tertiary end
        return result
    end
end

-- ZERO-ALLOCATION check: does packed value contain targetIdx?
-- Used in filter loops where DecodeColors would create thousands of temporary tables
local function PackedHasColor(packed, targetIdx)
    if not packed or packed <= 0 or not targetIdx then return false end
    if packed <= 12 then
        return packed == targetIdx
    elseif packed <= 1299 then
        return math.floor(packed / 100) == targetIdx or packed % 100 == targetIdx
    else
        if math.floor(packed / 10000) == targetIdx then return true end
        local remainder = packed % 10000
        return math.floor(remainder / 100) == targetIdx or remainder % 100 == targetIdx
    end
end

-- Get all color indices for a packed value (returns bitmask-like set)
-- Used for cache building where we need ALL colors, not just one match
local function PackedGetAllIndices(packed)
    if not packed or packed <= 0 then return nil end
    local result = {}
    if packed <= 12 then
        result[packed] = true
    elseif packed <= 1299 then
        local p = math.floor(packed / 100)
        local s = packed % 100
        result[p] = true
        if s > 0 and s <= 12 then result[s] = true end
    else
        local p = math.floor(packed / 10000)
        local rem = packed % 10000
        local s = math.floor(rem / 100)
        local t = rem % 100
        result[p] = true
        if s > 0 and s <= 12 then result[s] = true end
        if t > 0 and t <= 12 then result[t] = true end
    end
    return result
end

-- Name matching fallback
local NAME_WORDS = {
    [1]  = {"red", "crimson", "maroon", "blood", "ruby", "scarlet", "burgundy"},
    [2]  = {"orange", "copper", "amber", "burnt", "bronze", "brass", "fiery"},
    [3]  = {"yellow", "gold", "golden", "gilded", "saffron", "cream"},
    [4]  = {"green", "emerald", "jade", "olive", "moss", "verdant", "fel"},
    [5]  = {"teal", "cyan", "turquoise", "aqua"},
    [6]  = {"blue", "azure", "sapphire", "cerulean", "cobalt", "frost", "icy"},
    [7]  = {"purple", "violet", "lavender", "magenta", "plum", "amethyst"},
    [8]  = {"pink", "rose", "blush", "fuchsia"},
    [9]  = {"white", "ivory", "pearl", "snow", "silver", "sterling"},
    [10] = {"black", "ebony", "obsidian", "onyx", "sable", "shadow", "dark"},
    [11] = {"brown", "earthen", "chestnut", "umber", "tanned", "hide", "beige"},
    [12] = {"gray", "grey", "iron", "steel", "stone", "slate", "ash"},
}

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

function CS:GetColorIndices(visualID)
    local colorMap = ETB.ColorMap
    if not colorMap or not visualID then return nil end
    local packed = colorMap[visualID]
    if not packed then return nil end
    return DecodeColors(packed)
end

function CS:MatchName(name)
    if not name or type(name) ~= "string" or name:find("^Item %d") then return nil end
    local lower = name:lower()
    for idx, words in pairs(NAME_WORDS) do
        for _, word in ipairs(words) do
            if lower:find(word, 1, true) then return { idx } end
        end
    end
    return nil
end

function CS:GetItemColors(visualID, itemName)
    local colors = self:GetColorIndices(visualID)
    if colors then return colors end
    return self:MatchName(itemName)
end

--- Check if ANY of the item's colors match the target category
--- ZERO ALLOCATION: uses direct arithmetic on packed value
function CS:ItemMatchesCategory(visualID, itemName, categoryID)
    local cat = self.CategoryByID[categoryID]
    if not cat then return false end
    local targetIdx = cat.idx
    
    -- Direct packed check — no table creation
    local colorMap = ETB.ColorMap
    if colorMap and visualID then
        local packed = colorMap[visualID]
        if packed then
            return PackedHasColor(packed, targetIdx)
        end
    end
    
    -- Name fallback (only if no ColorMap data)
    if itemName and type(itemName) == "string" and not itemName:find("^Item %d") then
        local lower = itemName:lower()
        local words = NAME_WORDS[targetIdx]
        if words then
            for _, word in ipairs(words) do
                if lower:find(word, 1, true) then return true end
            end
        end
    end
    
    return false
end

--- Expose PackedHasColor for external use (SetsUI cache building)
function CS:VisualHasColor(visualID, colorIdx)
    local colorMap = ETB.ColorMap
    if not colorMap or not visualID then return false end
    local packed = colorMap[visualID]
    if not packed then return false end
    return PackedHasColor(packed, colorIdx)
end

--- Get all color indices for a visualID as a set table {idx=true,...}
--- Used for cache building (one allocation, reused many times)
function CS:GetColorSet(visualID)
    local colorMap = ETB.ColorMap
    if not colorMap or not visualID then return nil end
    local packed = colorMap[visualID]
    if not packed then return nil end
    return PackedGetAllIndices(packed)
end

function CS:FilterByColor(items, categoryID)
    if not categoryID or categoryID == "ALL" then return items end
    local filtered = {}
    for _, item in ipairs(items) do
        local vid = item.visualID or item.appearanceID
        if self:ItemMatchesCategory(vid, item.name, categoryID) then
            filtered[#filtered + 1] = item
        end
    end
    return filtered
end

function CS:GetSwatchColor(visualID, itemName)
    local colors = self:GetItemColors(visualID, itemName)
    if colors and colors[1] then
        local cat = self.CategoryByIdx[colors[1]]
        if cat then return cat.swatch[1], cat.swatch[2], cat.swatch[3] end
    end
    return nil
end

function CS:GetAllSwatches(visualID, itemName)
    local colors = self:GetItemColors(visualID, itemName)
    if not colors then return nil end
    local swatches = {}
    for _, idx in ipairs(colors) do
        local cat = self.CategoryByIdx[idx]
        if cat then
            swatches[#swatches + 1] = { cat.swatch[1], cat.swatch[2], cat.swatch[3], cat.name }
        end
    end
    return #swatches > 0 and swatches or nil
end

function CS:GetCategories()
    return self.Categories
end

function CS:HasData()
    return ETB.ColorMap ~= nil
end

function CS:GetStats()
    -- Trigger lazy load if needed
    if ETB._ColorMapLoader then
        ETB._ColorMapLoader()
        ETB._ColorMapLoader = nil
    end
    local n, m = 0, 0
    if ETB.ColorMap then
        for _, v in pairs(ETB.ColorMap) do n = n + 1; if v > 12 then m = m + 1 end end
    end
    return { mapEntries = n, multiColor = m, presets = #self.Categories }
end

ETB:Debug("ColorSearch", "ColorSearch loaded (ColorMap deferred)")
