--[[
    Commander 3.0 - Formatter System

    Handles converting typed objects into display strings/frames.
    Separates data (pipeline) from presentation (render).
]]

local ADDON_NAME, C3 = ...

-------------------------------------------------------------------------------
-- Formatter Registry
-------------------------------------------------------------------------------

local Formatter = {}
C3.Formatter = Formatter

-- Registered formatters by type
Formatter._formatters = {}

-- Default format modes
Formatter.MODE = {
    TEXT = "text",       -- Plain text (default)
    LIST = "list",       -- One item per line
    TABLE = "table",     -- Tabular with columns
    GRID = "grid",       -- Icon grid
    JSON = "json",       -- JSON output
    CSV = "csv",         -- CSV output
    COMPACT = "compact", -- Single line summary
}

-- Current mode
Formatter._mode = Formatter.MODE.TEXT

-------------------------------------------------------------------------------
-- Formatter Registration
-------------------------------------------------------------------------------

---Register a formatter for a type
---@param typeName string
---@param formatter table|function
function Formatter:Register(typeName, formatter)
    if type(formatter) == "function" then
        formatter = { format = formatter }
    end
    self._formatters[typeName] = formatter
end

---Get formatter for a type
---@param typeName string
---@return table?
function Formatter:Get(typeName)
    return self._formatters[typeName]
end

-------------------------------------------------------------------------------
-- Format Mode
-------------------------------------------------------------------------------

---Set the format mode
---@param mode string
function Formatter:SetMode(mode)
    self._mode = mode
end

---Get the current format mode
---@return string
function Formatter:GetMode()
    return self._mode
end

-------------------------------------------------------------------------------
-- Formatting Functions
-------------------------------------------------------------------------------

---Format a single object
---@param obj any
---@param options? table
---@return string
function Formatter:Format(obj, options)
    options = options or {}
    local mode = options.mode or self._mode

    if obj == nil then
        return ""
    end

    -- Handle primitives
    if type(obj) ~= "table" then
        return tostring(obj)
    end

    -- Check for typed object
    local typeName = obj._type
    if typeName then
        -- Try type-specific formatter
        local formatter = self._formatters[typeName]
        if formatter then
            if formatter[mode] then
                return formatter[mode](obj, options)
            elseif formatter.format then
                return formatter.format(obj, options)
            end
        end

        -- Fall back to type's own format function
        local typeDef = C3.Types:Get(typeName)
        if typeDef and typeDef.format then
            return typeDef.format(obj, options)
        end
    end

    -- Handle arrays
    if obj[1] ~= nil then
        return self:FormatArray(obj, options)
    end

    -- Generic table formatting
    return self:FormatTable(obj, options)
end

---Format an array of objects
---@param arr table
---@param options? table
---@return string
function Formatter:FormatArray(arr, options)
    options = options or {}
    local mode = options.mode or self._mode
    local lines = {}

    if mode == Formatter.MODE.LIST or mode == Formatter.MODE.TEXT then
        for _, item in ipairs(arr) do
            table.insert(lines, self:Format(item, options))
        end
        return table.concat(lines, "\n")

    elseif mode == Formatter.MODE.TABLE then
        return self:FormatAsTable(arr, options)

    elseif mode == Formatter.MODE.COMPACT then
        local parts = {}
        for i, item in ipairs(arr) do
            if i > 5 then
                table.insert(parts, "...")
                break
            end
            table.insert(parts, self:Format(item, { mode = Formatter.MODE.COMPACT }))
        end
        return table.concat(parts, ", ")

    elseif mode == Formatter.MODE.JSON then
        return self:FormatAsJSON(arr, options)

    elseif mode == Formatter.MODE.CSV then
        return self:FormatAsCSV(arr, options)

    else
        -- Default to list
        for _, item in ipairs(arr) do
            table.insert(lines, self:Format(item, options))
        end
        return table.concat(lines, "\n")
    end
end

---Format as table with columns
---@param arr table
---@param options? table
---@return string
function Formatter:FormatAsTable(arr, options)
    options = options or {}

    if #arr == 0 then
        return "(empty)"
    end

    -- Determine columns from first item
    local columns = options.columns
    if not columns then
        local first = arr[1]
        if type(first) == "table" then
            columns = {}
            for key in pairs(first) do
                if key:sub(1, 1) ~= "_" then -- Skip meta fields
                    table.insert(columns, key)
                end
            end
            table.sort(columns)
        else
            columns = { "value" }
        end
    end

    -- Calculate column widths
    local widths = {}
    for _, col in ipairs(columns) do
        widths[col] = #col
    end

    for _, item in ipairs(arr) do
        for _, col in ipairs(columns) do
            local val = type(item) == "table" and item[col] or item
            local len = #tostring(val or "")
            widths[col] = math.max(widths[col], math.min(len, 30))
        end
    end

    -- Build header
    local header = {}
    local separator = {}
    for _, col in ipairs(columns) do
        local w = widths[col]
        table.insert(header, string.format("%-" .. w .. "s", col:sub(1, w)))
        table.insert(separator, string.rep("-", w))
    end

    local lines = {
        table.concat(header, " | "),
        table.concat(separator, "-+-"),
    }

    -- Build rows
    for _, item in ipairs(arr) do
        local row = {}
        for _, col in ipairs(columns) do
            local val = type(item) == "table" and item[col] or item
            local w = widths[col]
            local str = tostring(val or ""):sub(1, w)
            table.insert(row, string.format("%-" .. w .. "s", str))
        end
        table.insert(lines, table.concat(row, " | "))
    end

    return table.concat(lines, "\n")
end

---Format as JSON
---@param obj any
---@param options? table
---@return string
function Formatter:FormatAsJSON(obj, options)
    options = options or {}
    local indent = options.indent or 0
    local prefix = string.rep("  ", indent)

    if obj == nil then
        return "null"
    elseif type(obj) == "boolean" then
        return obj and "true" or "false"
    elseif type(obj) == "number" then
        return tostring(obj)
    elseif type(obj) == "string" then
        -- Escape string
        local escaped = obj:gsub('\\', '\\\\'):gsub('"', '\\"'):gsub('\n', '\\n')
        return '"' .. escaped .. '"'
    elseif type(obj) == "table" then
        -- Check if array
        if obj[1] ~= nil or next(obj) == nil then
            local items = {}
            for _, v in ipairs(obj) do
                table.insert(items, self:FormatAsJSON(v, { indent = indent + 1 }))
            end
            if #items == 0 then
                return "[]"
            elseif #items <= 3 and not options.pretty then
                return "[" .. table.concat(items, ", ") .. "]"
            else
                return "[\n" .. prefix .. "  " ..
                    table.concat(items, ",\n" .. prefix .. "  ") ..
                    "\n" .. prefix .. "]"
            end
        else
            -- Object
            local items = {}
            for k, v in pairs(obj) do
                if type(k) == "string" and k:sub(1, 1) ~= "_" then
                    table.insert(items, '"' .. k .. '": ' ..
                        self:FormatAsJSON(v, { indent = indent + 1 }))
                end
            end
            table.sort(items)
            if #items == 0 then
                return "{}"
            elseif #items <= 2 and not options.pretty then
                return "{" .. table.concat(items, ", ") .. "}"
            else
                return "{\n" .. prefix .. "  " ..
                    table.concat(items, ",\n" .. prefix .. "  ") ..
                    "\n" .. prefix .. "}"
            end
        end
    else
        return '"' .. tostring(obj) .. '"'
    end
end

---Format as CSV
---@param arr table
---@param options? table
---@return string
function Formatter:FormatAsCSV(arr, options)
    options = options or {}

    if #arr == 0 then
        return ""
    end

    -- Determine columns
    local columns = options.columns
    if not columns then
        local first = arr[1]
        if type(first) == "table" then
            columns = {}
            for key in pairs(first) do
                if key:sub(1, 1) ~= "_" then
                    table.insert(columns, key)
                end
            end
            table.sort(columns)
        else
            columns = { "value" }
        end
    end

    local function escapeCSV(val)
        val = tostring(val or "")
        if val:find('[,"\n]') then
            return '"' .. val:gsub('"', '""') .. '"'
        end
        return val
    end

    local lines = {}

    -- Header
    if options.header ~= false then
        table.insert(lines, table.concat(columns, ","))
    end

    -- Rows
    for _, item in ipairs(arr) do
        local row = {}
        for _, col in ipairs(columns) do
            local val = type(item) == "table" and item[col] or item
            table.insert(row, escapeCSV(val))
        end
        table.insert(lines, table.concat(row, ","))
    end

    return table.concat(lines, "\n")
end

---Format a generic table
---@param tbl table
---@param options? table
---@return string
function Formatter:FormatTable(tbl, options)
    options = options or {}
    local lines = {}

    for k, v in pairs(tbl) do
        if type(k) == "string" and k:sub(1, 1) ~= "_" then
            table.insert(lines, string.format("  %s: %s", k, tostring(v)))
        end
    end

    table.sort(lines)
    return table.concat(lines, "\n")
end

-------------------------------------------------------------------------------
-- Tree Formatting (Box-Drawing Characters)
-------------------------------------------------------------------------------

-- Box Drawing Characters (UTF-8 byte sequences)
-- These render correctly with JetBrains Mono, Fira Code, and similar fonts
Formatter.TREE = {
    -- Vertical line │ (U+2502)
    V = "\226\148\130",
    -- T-junction ├ (U+251C)
    T = "\226\148\156",
    -- L-corner └ (U+2514)
    L = "\226\148\148",
    -- Horizontal ─ (U+2500)
    H = "\226\148\128",
}

-- Pre-built tree prefixes
Formatter.TREE_PREFIX = {
    ITEM = nil,      -- "├── " (built lazily)
    LAST = nil,      -- "└── " (built lazily)
    CHILD = nil,     -- "│   " (built lazily)
    EMPTY = "    ",  -- "    " (4 spaces for non-tree indent)
}

-- ASCII tree prefixes (fallback - renders correctly in WoW unlike box-drawing chars)
Formatter.ASCII_PREFIX = {
    ITEM = "|-- ",   -- ASCII tree branch
    LAST = "`-- ",   -- ASCII tree end
    CHILD = "|   ",  -- ASCII tree continuation
    EMPTY = "    ",  -- 4 spaces
}

-- Simple indent prefixes (minimal fallback)
Formatter.SIMPLE_PREFIX = {
    ITEM = "  ",     -- 2 spaces
    LAST = "  ",     -- 2 spaces
    CHILD = "  ",    -- 2 spaces
    EMPTY = "  ",    -- 2 spaces
}

---Initialize tree prefixes (call after verifying font support)
function Formatter:InitTreePrefixes()
    local T = self.TREE
    self.TREE_PREFIX.ITEM = T.T .. T.H .. T.H .. " "   -- "├── "
    self.TREE_PREFIX.LAST = T.L .. T.H .. T.H .. " "   -- "└── "
    self.TREE_PREFIX.CHILD = T.V .. "   "              -- "│   "
end

-- Initialize on load
Formatter:InitTreePrefixes()

---Check if tree characters should be used
---@return boolean
function Formatter:UseTreeCharacters()
    local theme = C3.UI and C3.UI.Theme
    return theme and theme.useTreeCharacters ~= false
end

---Get the appropriate prefix set based on theme setting
---@return table
function Formatter:GetPrefixSet()
    if self:UseTreeCharacters() then
        return self.TREE_PREFIX
    else
        return self.SIMPLE_PREFIX
    end
end

---Format a hierarchical tree structure
---@param nodes table Array of root nodes
---@param getChildren function(node) -> table Returns array of child nodes
---@param getLabel function(node) -> string Returns display string for node
---@param options? table { maxDepth, maxItems }
---@return table lines Array of formatted strings
function Formatter:FormatTree(nodes, getChildren, getLabel, options)
    options = options or {}
    local maxDepth = options.maxDepth or 10
    local maxItems = options.maxItems or 100
    local lines = {}
    local itemCount = 0

    local prefix = self:GetPrefixSet()

    local function traverse(list, depth, parentPrefix)
        if depth > maxDepth then return end

        for i, node in ipairs(list) do
            if itemCount >= maxItems then
                local remaining = #list - i + 1
                if remaining > 0 then
                    table.insert(lines, parentPrefix .. self:Colorize(
                        string.format("... and %d more", remaining), "4C566A"))
                end
                return
            end

            local isLast = (i == #list)
            local connector = isLast and prefix.LAST or prefix.ITEM
            local label = getLabel(node)

            table.insert(lines, parentPrefix .. connector .. label)
            itemCount = itemCount + 1

            -- Process children
            local children = getChildren(node)
            if children and #children > 0 then
                local childPrefix = parentPrefix .. (isLast and prefix.EMPTY or prefix.CHILD)
                traverse(children, depth + 1, childPrefix)
            end
        end
    end

    traverse(nodes, 0, "")
    return lines
end

---Format a simple list as a tree (single level)
---@param items table Array of items
---@param getLabel function(item) -> string
---@param options? table
---@return table lines
function Formatter:FormatSimpleTree(items, getLabel, options)
    return self:FormatTree(items, function() return nil end, getLabel, options)
end

-------------------------------------------------------------------------------
-- Color Formatting
-------------------------------------------------------------------------------

local QUALITY_COLORS = {
    [0] = "9d9d9d", -- Poor
    [1] = "ffffff", -- Common
    [2] = "1eff00", -- Uncommon
    [3] = "0070dd", -- Rare
    [4] = "a335ee", -- Epic
    [5] = "ff8000", -- Legendary
    [6] = "e6cc80", -- Artifact
    [7] = "00ccff", -- Heirloom
    [8] = "00ccff", -- WoW Token
}

---Colorize text
---@param text string
---@param color string Hex color (without #)
---@return string
function Formatter:Colorize(text, color)
    return string.format("|cFF%s%s|r", color, text)
end

---Colorize by quality
---@param text string
---@param quality number
---@return string
function Formatter:ColorByQuality(text, quality)
    local color = QUALITY_COLORS[quality] or "ffffff"
    return self:Colorize(text, color)
end

-------------------------------------------------------------------------------
-- Progress Bars
-------------------------------------------------------------------------------

-- Unicode block characters for smooth progress bars
-- Using ASCII-safe characters that WoW renders well
local PROGRESS_CHARS = {
    full = "|",      -- Full block (using pipe for compatibility)
    empty = ":",     -- Empty block
}

---Create a text-based progress bar
---@param current number Current value
---@param max number Maximum value
---@param width? number Bar width in characters (default 20)
---@param options? table { showPercent, showValues, colorByPercent, colors }
---@return string
function Formatter:ProgressBar(current, max, width, options)
    width = width or 20
    options = options or {}

    -- Calculate percentage
    local percent = max > 0 and (current / max * 100) or 0
    percent = math.max(0, math.min(100, percent))

    -- Calculate filled width
    local filledWidth = math.floor(width * percent / 100)
    local emptyWidth = width - filledWidth

    -- Build the bar
    local filled = string.rep(PROGRESS_CHARS.full, filledWidth)
    local empty = string.rep(PROGRESS_CHARS.empty, emptyWidth)

    -- Determine color based on percentage (using Nord colors)
    local color
    if options.colorByPercent ~= false then
        if percent >= 75 then
            color = options.colors and options.colors.high or "A3BE8C"  -- nord14 green
        elseif percent >= 25 then
            color = options.colors and options.colors.medium or "EBCB8B"  -- nord13 yellow
        else
            color = options.colors and options.colors.low or "BF616A"  -- nord11 red
        end
    else
        color = options.color or "88C0D0"  -- nord8 cyan (default)
    end

    -- Build result
    local bar = "[" .. self:Colorize(filled, color) .. self:Colorize(empty, "444444") .. "]"

    -- Add percentage text
    if options.showPercent ~= false then
        bar = bar .. " " .. self:Colorize(string.format("%d%%", percent), color)
    end

    -- Add values
    if options.showValues then
        bar = bar .. string.format(" (%d/%d)", current, max)
    end

    return bar
end

---Create a compact inline progress bar
---@param percent number Percentage (0-100)
---@param width? number Width (default 10)
---@return string
function Formatter:MiniBar(percent, width)
    width = width or 10
    percent = math.max(0, math.min(100, percent))
    local filled = math.floor(width * percent / 100)
    local empty = width - filled

    -- Nord colors: green/yellow/red
    local color = percent >= 75 and "A3BE8C" or (percent >= 25 and "EBCB8B" or "BF616A")

    return self:Colorize(string.rep("|", filled), color) ..
           self:Colorize(string.rep(":", empty), "4C566A")
end

-------------------------------------------------------------------------------
-- Status Indicators
-------------------------------------------------------------------------------

-- Status symbols (using Nord theme colors)
-- nord14 green (A3BE8C), nord11 red (BF616A), nord13 yellow (EBCB8B)
-- nord8 cyan (88C0D0), nord3 gray (4C566A), nord4 light (D8DEE9)
Formatter.STATUS = {
    OK = "|cFFA3BE8C+|r",       -- nord14 green
    FAIL = "|cFFBF616Ax|r",     -- nord11 red
    WARN = "|cFFEBCB8B!|r",     -- nord13 yellow
    INFO = "|cFF88C0D0*|r",     -- nord8 cyan
    NONE = "|cFF4C566A-|r",     -- nord3 gray
    ACTIVE = "|cFFA3BE8C*|r",   -- nord14 green
    INACTIVE = "|cFF4C566Ao|r", -- nord3 gray
    ARROW_RIGHT = "|cFF81A1C1o|r", -- nord9 blue
    ARROW_DOWN = "|cFF81A1C1v|r",  -- nord9 blue
    CHECK = "|cFFA3BE8Cv|r",    -- nord14 green
    CROSS = "|cFFBF616Ax|r",    -- nord11 red
    STAR = "|cFFEBCB8B*|r",     -- nord13 yellow
    DOT = "|cFFD8DEE9.|r",      -- nord4 light
}

---Get a status indicator
---@param status string Status key or boolean
---@return string
function Formatter:Status(status)
    if type(status) == "boolean" then
        return status and self.STATUS.OK or self.STATUS.FAIL
    end
    return self.STATUS[status:upper()] or self.STATUS.NONE
end

---Format a labeled value with optional status
---@param label string
---@param value any
---@param status? string|boolean
---@return string
function Formatter:LabeledValue(label, value, status)
    local statusStr = status and (self:Status(status) .. " ") or ""
    local labelStr = self:Colorize(label .. ":", "AAAAAA")
    return statusStr .. labelStr .. " " .. tostring(value)
end

-------------------------------------------------------------------------------
-- Aligned Columns
-------------------------------------------------------------------------------

---Format multiple values as aligned columns
---@param columns table Array of column definitions { value, width?, align?, color? }
---@param separator? string Column separator (default "  ")
---@return string
function Formatter:Columns(columns, separator)
    separator = separator or "  "
    local parts = {}

    for _, col in ipairs(columns) do
        local value = tostring(col.value or col[1] or "")
        local width = col.width or col[2] or #value
        local align = col.align or col[3] or "left"
        local color = col.color or col[4]

        -- Strip color codes for length calculation
        local plainValue = value:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):gsub("|T.-|t", "  ")
        local padding = width - #plainValue

        local padded
        if padding <= 0 then
            padded = value
        elseif align == "right" then
            padded = string.rep(" ", padding) .. value
        elseif align == "center" then
            local left = math.floor(padding / 2)
            padded = string.rep(" ", left) .. value .. string.rep(" ", padding - left)
        else
            padded = value .. string.rep(" ", padding)
        end

        if color then
            padded = self:Colorize(padded, color)
        end

        table.insert(parts, padded)
    end

    return table.concat(parts, separator)
end

---Format a key-value pair list with aligned values
---@param pairs table Array of { key, value } or { key = value }
---@param options? table { keyWidth, separator, keyColor, valueColor }
---@return string
function Formatter:KeyValueList(pairs, options)
    options = options or {}
    local keyWidth = options.keyWidth or 0
    local keyColor = options.keyColor or "AAAAAA"
    local separator = options.separator or ": "

    -- Calculate max key width if not specified
    if keyWidth == 0 then
        for _, pair in ipairs(pairs) do
            local key = pair.key or pair[1] or ""
            keyWidth = math.max(keyWidth, #key)
        end
    end

    local lines = {}
    for _, pair in ipairs(pairs) do
        local key = pair.key or pair[1] or ""
        local value = pair.value or pair[2] or ""
        local padded = key .. string.rep(" ", keyWidth - #key)
        table.insert(lines, self:Colorize(padded, keyColor) .. separator .. tostring(value))
    end

    return table.concat(lines, "\n")
end

-------------------------------------------------------------------------------
-- Icon Helpers
-------------------------------------------------------------------------------

---Get default icon size based on theme font size
---@return number
function Formatter:GetIconSize()
    local fontSize = C3.UI and C3.UI.Theme and C3.UI.Theme.FontSize or 12
    return fontSize + 2  -- Icon slightly larger than font
end

---Get vertical offset to align icon with text
---WoW inline textures have no anchoring - y-offset relative to baseline is the only control.
---Negative values push the icon down. We use 2/3 of icon height to align icon center
---roughly with the text's visual center (accounting for descenders).
---@param size number Icon size
---@return number Negative offset to push icon down
function Formatter:GetIconOffset(size)
    return -(size - 2)  -- -12 for size 14
end

---Format an icon texture with proper alignment
---@param iconId number|string Texture ID or path
---@param size? number Icon size (default from theme)
---@return string
function Formatter:Icon(iconId, size)
    if not iconId then return "" end
    size = size or self:GetIconSize()
    local offset = self:GetIconOffset(size)
    return string.format("|T%s:%d:%d:0:%d|t", tostring(iconId), size, size, offset)
end

---Format an icon from texture path with proper alignment
---@param path string Texture path (e.g., "Interface\\Icons\\...")
---@param size? number Icon size (default from theme)
---@return string
function Formatter:IconWithPath(path, size)
    if not path then return "" end
    size = size or self:GetIconSize()
    local offset = self:GetIconOffset(size)
    return string.format("|T%s:%d:%d:0:%d|t", path, size, size, offset)
end

---Format an icon with text
---@param iconId number|string
---@param text string
---@param size? number
---@return string
function Formatter:IconText(iconId, text, size)
    local icon = self:Icon(iconId, size)
    if icon ~= "" then
        return icon .. " " .. text
    end
    return text
end

-------------------------------------------------------------------------------
-- Built-in Formatters
-------------------------------------------------------------------------------

-- Text formatter
Formatter:Register("Text", {
    format = function(obj)
        return obj.content or ""
    end
})

-- Error formatter
Formatter:Register("Error", {
    format = function(obj)
        return Formatter:Colorize("Error: " .. (obj.message or "Unknown error"), "BF616A")  -- nord11 red
    end
})

-- Item formatter
Formatter:Register("Item", {
    text = function(obj)
        return obj.link or obj.name or "Unknown Item"
    end,
    list = function(obj)
        local quality = obj.quality or 1
        local name = obj.name or "Unknown"
        local ilvl = obj.ilvl and obj.ilvl > 0 and (" (ilvl " .. obj.ilvl .. ")") or ""
        return Formatter:ColorByQuality(name, quality) .. ilvl
    end,
    table = function(obj)
        return obj.name or "Unknown"
    end,
    compact = function(obj)
        return obj.link or obj.name or "?"
    end,
    format = function(obj)
        return obj.link or obj.name or "Unknown Item"
    end
})

-- Number formatter
Formatter:Register("Number", {
    format = function(obj)
        return tostring(obj.value or 0)
    end
})

-- FileEntry formatter
Formatter:Register("FileEntry", {
    format = function(obj)
        return obj.display or obj.name or obj.path
    end
})

-- Group formatter
Formatter:Register("Group", {
    format = function(obj)
        return obj.display or string.format("%s (%d)", obj.key, obj.count)
    end
})

-- Durability formatter
Formatter:Register("Durability", {
    format = function(obj)
        local current = obj.current or 0
        local max = obj.max or 100
        local percent = obj.percent or (max > 0 and math.floor(current / max * 100) or 0)
        local slotName = obj.slotName or "?"

        -- Format: SlotName [||||||::::] 75%
        local bar = Formatter:MiniBar(percent, 10)
        local slotPadded = slotName .. string.rep(" ", 12 - #slotName)

        return Formatter:Colorize(slotPadded, "81A1C1") .. " " .. bar ..
               " " .. Formatter:Colorize(string.format("%3d%%", percent),
               percent >= 75 and "A3BE8C" or (percent >= 25 and "EBCB8B" or "BF616A"))
    end,
    list = function(obj)
        local current = obj.current or 0
        local max = obj.max or 100
        local percent = obj.percent or (max > 0 and math.floor(current / max * 100) or 0)

        return Formatter:Columns({
            { obj.slotName or "?", 12 },
            { Formatter:ProgressBar(current, max, 15, { showPercent = true, showValues = false }) },
        })
    end
})

-- EmptySlot formatter
Formatter:Register("EmptySlot", {
    format = function(obj)
        return Formatter:Colorize(obj.display or "(empty)", "4C566A")  -- nord3
    end
})

-- Mount formatter
Formatter:Register("Mount", {
    format = function(obj)
        local prefix = ""
        if obj.isActive then
            prefix = Formatter:Colorize("* ", "A3BE8C")  -- nord14 green
        elseif obj.isFavorite then
            prefix = Formatter:Colorize("* ", "EBCB8B")  -- nord13 yellow
        end

        local icon = obj.icon and (Formatter:Icon(obj.icon) .. " ") or ""
        local name = obj.name or "Unknown Mount"

        if not obj.isUsable then
            name = Formatter:Colorize(name, "4C566A")  -- nord3 gray
        end

        local extra = ""
        if obj.isFlying then
            extra = Formatter:Colorize(" [Flying]", "87CEEB")
        end

        return prefix .. icon .. name .. extra
    end,
    list = function(obj)
        local icon = obj.icon and (Formatter:Icon(obj.icon) .. " ") or ""
        local name = obj.name or "Unknown"
        local color = obj.isUsable and "FFFFFF" or "888888"
        return icon .. Formatter:Colorize(name, color)
    end,
    compact = function(obj)
        return obj.name or "?"
    end
})

-------------------------------------------------------------------------------
-- Format Command
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "format",
    description = "Set output format mode",
    category = "utility",
    acceptsInput = false,
    args = {
        {
            name = "mode",
            required = false,
            description = "Format mode: text, list, table, grid, json, csv, compact",
            choices = { "text", "list", "table", "grid", "json", "csv", "compact" }
        }
    },
    execute = function(ctx)
        local mode = ctx:GetArg(1)

        if not mode then
            return ctx:Text("Current format: " .. Formatter:GetMode())
        end

        Formatter:SetMode(mode)
        return ctx:Text("Format set to: " .. mode)
    end
})

return Formatter
