--[[
    Commander 3.0 - Color Validation

    Centralized color parsing, validation, and intellisense provider.
    Supports: hex codes, named colors, class colors, quality colors, theme keys.
]]

local ADDON_NAME, C3 = ...

local ColorValidation = {}
C3.ColorValidation = ColorValidation

-- Use shared utilities
local fuzzyMatch = function(...) return C3.StringUtils.fuzzyMatch(...) end
local MAX_SUGGESTIONS = C3.Constants.MAX_SUGGESTIONS

-------------------------------------------------------------------------------
-- Color Tables
-------------------------------------------------------------------------------

-- CSS-like basic named colors
ColorValidation.NAMED_COLORS = {
    -- Primary colors
    red = "FF0000",
    green = "00FF00",
    blue = "0000FF",
    -- Secondary colors
    yellow = "FFFF00",
    cyan = "00FFFF",
    magenta = "FF00FF",
    -- Neutrals
    white = "FFFFFF",
    black = "000000",
    gray = "808080",
    grey = "808080",  -- British spelling alias
    -- Extended colors
    orange = "FF8000",
    pink = "FF69B4",
    purple = "800080",
    gold = "FFD700",
    silver = "C0C0C0",
    brown = "8B4513",
    navy = "000080",
    teal = "008080",
    lime = "32CD32",
    coral = "FF7F50",
    salmon = "FA8072",
    crimson = "DC143C",
    violet = "EE82EE",
    indigo = "4B0082",
    aqua = "00FFFF",
    maroon = "800000",
    olive = "808000",
}

-- WoW class colors
ColorValidation.CLASS_COLORS = {
    warrior = "C79C6E",
    paladin = "F58CBA",
    hunter = "ABD473",
    rogue = "FFF569",
    priest = "FFFFFF",
    deathknight = "C41F3B",
    shaman = "0070DE",
    mage = "69CCF0",
    warlock = "9482C9",
    monk = "00FF96",
    druid = "FF7D0A",
    demonhunter = "A330C9",
    evoker = "33937F",
    -- Aliases
    dk = "C41F3B",
    dh = "A330C9",
}

-- WoW item quality colors
ColorValidation.QUALITY_COLORS = {
    poor = "9D9D9D",
    common = "FFFFFF",
    uncommon = "1EFF00",
    rare = "0070FF",
    epic = "A335EE",
    legendary = "FF8000",
    artifact = "E6CC80",
    heirloom = "00CCFF",
    -- Numeric aliases
    ["0"] = "9D9D9D",
    ["1"] = "FFFFFF",
    ["2"] = "1EFF00",
    ["3"] = "0070FF",
    ["4"] = "A335EE",
    ["5"] = "FF8000",
    ["6"] = "E6CC80",
    ["7"] = "00CCFF",
}

-- Semantic color shortcuts (map to theme, but with fallbacks)
ColorValidation.SEMANTIC_COLORS = {
    success = "A3BE8C",
    error = "BF616A",
    warning = "EBCB8B",
    info = "81A1C1",
    dim = "4C566A",
    highlight = "88C0D0",
    special = "B48EAD",
}

-------------------------------------------------------------------------------
-- Validation API
-------------------------------------------------------------------------------

---Validate and normalize a color input
---@param input string|nil Color input in various formats
---@return string|nil hex Normalized 6-character hex (without #) or nil if invalid
---@return string|nil colorType Type of color matched ("hex", "named", "class", "quality", "theme", "semantic")
function ColorValidation.Validate(input)
    if not input or input == "" then
        return nil, nil
    end

    local cleaned = strtrim(tostring(input))
    if cleaned == "" then
        return nil, nil
    end

    -- Try hex formats first

    -- Format: #RRGGBB or RRGGBB (6 hex digits)
    local hex = cleaned:match("^#?([0-9A-Fa-f]{6})$")
    if hex then
        return hex:upper(), "hex"
    end

    -- Format: #RGB (shorthand) -> expand to RRGGBB
    local shortHex = cleaned:match("^#?([0-9A-Fa-f]{3})$")
    if shortHex then
        local r, g, b = shortHex:sub(1, 1), shortHex:sub(2, 2), shortHex:sub(3, 3)
        return (r .. r .. g .. g .. b .. b):upper(), "hex"
    end

    -- Format: |cffRRGGBB or |cFFRRGGBB (WoW color code)
    local wowHex = cleaned:match("^|c[Ff][Ff]([0-9A-Fa-f]{6})")
    if wowHex then
        return wowHex:upper(), "hex"
    end

    -- Format: |cffAARRGGBB (with alpha, strip the alpha)
    local wowHexAlpha = cleaned:match("^|c([0-9A-Fa-f]{2})([0-9A-Fa-f]{6})")
    if wowHexAlpha then
        return wowHexAlpha:upper(), "hex"
    end

    -- Try named lookups (case insensitive)
    local lowerInput = cleaned:lower()

    -- Semantic colors (shortcuts)
    if ColorValidation.SEMANTIC_COLORS[lowerInput] then
        return ColorValidation.SEMANTIC_COLORS[lowerInput], "semantic"
    end

    -- Named colors
    if ColorValidation.NAMED_COLORS[lowerInput] then
        return ColorValidation.NAMED_COLORS[lowerInput], "named"
    end

    -- Class colors
    if ColorValidation.CLASS_COLORS[lowerInput] then
        return ColorValidation.CLASS_COLORS[lowerInput], "class"
    end

    -- Quality colors
    if ColorValidation.QUALITY_COLORS[lowerInput] then
        return ColorValidation.QUALITY_COLORS[lowerInput], "quality"
    end

    -- Try theme color keys (dotted paths like "accent.success")
    local themeColor = C3.Colors and C3.Colors.GetColor(cleaned)
    if themeColor and type(themeColor) == "string" then
        return themeColor:upper(), "theme"
    end

    -- Invalid color
    return nil, nil
end

---Check if a string is a valid color
---@param input string|nil Color input
---@return boolean
function ColorValidation.IsValid(input)
    local hex = ColorValidation.Validate(input)
    return hex ~= nil
end

-------------------------------------------------------------------------------
-- Conversion Helpers
-------------------------------------------------------------------------------

---Convert hex to RGB values (0-1 range)
---@param hex string 6-character hex string
---@return number r, number g, number b (0-1 range)
function ColorValidation.HexToRGB(hex)
    if not hex or #hex ~= 6 then
        return 1, 1, 1
    end
    local r = tonumber(hex:sub(1, 2), 16) / 255
    local g = tonumber(hex:sub(3, 4), 16) / 255
    local b = tonumber(hex:sub(5, 6), 16) / 255
    return r or 1, g or 1, b or 1
end

---Convert RGB to hex
---@param r number Red (0-1 or 0-255)
---@param g number Green (0-1 or 0-255)
---@param b number Blue (0-1 or 0-255)
---@return string hex 6-character uppercase hex string
function ColorValidation.RGBToHex(r, g, b)
    -- Normalize to 0-255 range
    if r <= 1 and g <= 1 and b <= 1 then
        r, g, b = r * 255, g * 255, b * 255
    end
    -- Clamp to valid range
    r = math.min(255, math.max(0, math.floor(r + 0.5)))
    g = math.min(255, math.max(0, math.floor(g + 0.5)))
    b = math.min(255, math.max(0, math.floor(b + 0.5)))
    return string.format("%02X%02X%02X", r, g, b)
end

---Create WoW color escape code
---@param hex string 6-character hex
---@return string WoW color code (e.g., "|cFFFF0000")
function ColorValidation.ToWoWColor(hex)
    return string.format("|cFF%s", hex:upper())
end

---Colorize text with a validated color
---@param text string Text to colorize
---@param colorInput string Color input (any valid format)
---@return string Colored text, or original if invalid
function ColorValidation.Colorize(text, colorInput)
    local hex = ColorValidation.Validate(colorInput)
    if hex then
        return string.format("|cFF%s%s|r", hex, text)
    end
    return text
end

-------------------------------------------------------------------------------
-- Color Discovery
-------------------------------------------------------------------------------

---Get all available colors with metadata
---@return table[] Array of { name, hex, category, priority }
function ColorValidation.GetAllColors()
    local colors = {}

    -- Theme colors (highest priority - dynamically discovered)
    local theme = C3.UI and C3.UI.Theme and C3.UI.Theme.colors
    if theme then
        local function walkTheme(tbl, prefix)
            for key, value in pairs(tbl) do
                local fullKey = prefix and (prefix .. "." .. key) or key
                if type(value) == "string" and value:match("^[0-9A-Fa-f]{6}$") then
                    colors[#colors + 1] = {
                        name = fullKey,
                        hex = value:upper(),
                        category = "theme",
                        priority = 100,
                    }
                elseif type(value) == "table" and not value[1] then
                    -- Recurse into nested tables (but not arrays like RGBA)
                    walkTheme(value, fullKey)
                end
            end
        end
        walkTheme(theme, nil)
    end

    -- Semantic colors (shortcuts)
    for name, hex in pairs(ColorValidation.SEMANTIC_COLORS) do
        colors[#colors + 1] = {
            name = name,
            hex = hex,
            category = "semantic",
            priority = 90,
        }
    end

    -- Named colors
    for name, hex in pairs(ColorValidation.NAMED_COLORS) do
        colors[#colors + 1] = {
            name = name,
            hex = hex,
            category = "named",
            priority = 80,
        }
    end

    -- Class colors (exclude aliases)
    for name, hex in pairs(ColorValidation.CLASS_COLORS) do
        if #name > 2 then  -- Skip dk, dh aliases
            colors[#colors + 1] = {
                name = name,
                hex = hex,
                category = "class",
                priority = 70,
            }
        end
    end

    -- Quality colors (named only, not numeric)
    for name, hex in pairs(ColorValidation.QUALITY_COLORS) do
        if not tonumber(name) then
            colors[#colors + 1] = {
                name = name,
                hex = hex,
                category = "quality",
                priority = 60,
            }
        end
    end

    return colors
end

-------------------------------------------------------------------------------
-- Intellisense Provider
-------------------------------------------------------------------------------

-- Block character for color preview
local COLOR_SWATCH = "\226\150\136"  -- █ (Unicode U+2588)

-- Register the Colors intellisense provider
-- Note: This file is loaded after intellisense.lua in the TOC
if C3.Intellisense and C3.Intellisense.RegisterProvider then
    C3.Intellisense:RegisterProvider("Colors", function(partial, ctx)
        local results = {}
        partial = partial or ""

        -- Check if user is typing a hex code
        local hexMatch = partial:match("^#?([0-9A-Fa-f]+)$")
        if hexMatch and #hexMatch >= 1 then
            -- If it's a complete or partial hex code, validate and show preview
            local validated, colorType = ColorValidation.Validate(partial)
            if validated then
                local preview = string.format("|cFF%s%s|r", validated, COLOR_SWATCH)
                results[#results + 1] = {
                    text = "#" .. validated,
                    description = preview .. " Hex color",
                    _hex = validated,
                    _category = "hex",
                }
            elseif #hexMatch == 6 then
                -- Complete 6-digit hex, just needs formatting
                local preview = string.format("|cFF%s%s|r", hexMatch:upper(), COLOR_SWATCH)
                results[#results + 1] = {
                    text = "#" .. hexMatch:upper(),
                    description = preview .. " Hex color",
                    _hex = hexMatch:upper(),
                    _category = "hex",
                }
            end
        end

        -- Get all available colors
        local allColors = ColorValidation.GetAllColors()

        -- Filter using fuzzy matching
        local matched = {}
        for _, color in ipairs(allColors) do
            if partial == "" or fuzzyMatch(partial, color.name) then
                matched[#matched + 1] = color
            end
        end

        -- Sort by priority (descending), then by name length (ascending)
        table.sort(matched, function(a, b)
            if a.priority ~= b.priority then
                return a.priority > b.priority
            end
            return #a.name < #b.name
        end)

        -- Build result suggestions (limit to MAX_SUGGESTIONS)
        local count = #results
        for i, color in ipairs(matched) do
            if count >= MAX_SUGGESTIONS then break end

            local preview = string.format("|cFF%s%s|r", color.hex, COLOR_SWATCH)
            local category = color.category

            results[#results + 1] = {
                text = color.name,
                description = string.format("%s #%s (%s)", preview, color.hex, category),
                _hex = color.hex,
                _category = category,
            }
            count = count + 1
        end

        return results
    end)
end

return ColorValidation
