--[[
    Commander 3.0 - TUI Widget Utilities

    Helper functions for widget rendering.
    Widgets are stateless - they receive data as props and render to a buffer.

    This module provides common utilities:
    - Text alignment (center, right)
    - Text truncation with ellipsis
    - UTF-8 width calculation
]]

local ADDON_NAME, C3 = ...

C3.TUI = C3.TUI or {}

-------------------------------------------------------------------------------
-- Widget Utilities
-------------------------------------------------------------------------------

local Widget = {}
C3.TUI.Widget = Widget

---Get visual width of a string (UTF-8 aware)
---Note: Approximation - assumes monospace font where each grapheme = 1 cell
---@param text string
---@return number
function Widget.TextWidth(text)
    if not text then return 0 end

    local count = 0
    -- Count grapheme clusters (UTF-8 sequences)
    for _ in text:gmatch("[%z\1-\127\194-\244][\128-\191]*") do
        count = count + 1
    end
    return count
end

---Truncate text with ellipsis if too long
---@param text string
---@param maxWidth number
---@param ellipsis? string Ellipsis string (default "...")
---@return string
function Widget.Truncate(text, maxWidth, ellipsis)
    if not text then return "" end

    ellipsis = ellipsis or "..."
    local textWidth = Widget.TextWidth(text)

    if textWidth <= maxWidth then
        return text
    end

    local ellipsisWidth = Widget.TextWidth(ellipsis)
    if maxWidth <= ellipsisWidth then
        return text:sub(1, maxWidth)
    end

    -- Collect characters up to maxWidth - ellipsisWidth
    local result = {}
    local count = 0
    local targetWidth = maxWidth - ellipsisWidth

    for char in text:gmatch("[%z\1-\127\194-\244][\128-\191]*") do
        if count >= targetWidth then break end
        result[#result + 1] = char
        count = count + 1
    end

    return table.concat(result) .. ellipsis
end

---Pad text to fixed width
---@param text string
---@param width number
---@param align? string "left", "center", "right" (default "left")
---@param padChar? string Character to pad with (default " ")
---@return string
function Widget.Pad(text, width, align, padChar)
    if not text then text = "" end
    align = align or "left"
    padChar = padChar or " "

    local textWidth = Widget.TextWidth(text)
    if textWidth >= width then
        return Widget.Truncate(text, width)
    end

    local padding = width - textWidth

    if align == "center" then
        local leftPad = math.floor(padding / 2)
        local rightPad = padding - leftPad
        return string.rep(padChar, leftPad) .. text .. string.rep(padChar, rightPad)
    elseif align == "right" then
        return string.rep(padChar, padding) .. text
    else  -- left
        return text .. string.rep(padChar, padding)
    end
end

---Render text centered horizontally within a rect
---@param buf table TuiBuffer
---@param rect table Rect
---@param text string
---@param fg? string
function Widget.CenterText(buf, rect, text, fg)
    local textWidth = Widget.TextWidth(text)
    local startX = rect.x + math.floor((rect.width - textWidth) / 2)
    startX = math.max(rect.x, startX)
    buf:SetString(startX, rect.y, text, fg, rect.width)
end

---Render text right-aligned within a rect
---@param buf table TuiBuffer
---@param rect table Rect
---@param text string
---@param fg? string
function Widget.RightText(buf, rect, text, fg)
    local textWidth = Widget.TextWidth(text)
    local startX = rect.x + rect.width - textWidth
    startX = math.max(rect.x, startX)
    buf:SetString(startX, rect.y, text, fg, rect.width)
end

---Render text left-aligned within a rect
---@param buf table TuiBuffer
---@param rect table Rect
---@param text string
---@param fg? string
function Widget.LeftText(buf, rect, text, fg)
    buf:SetString(rect.x, rect.y, text, fg, rect.width)
end

---Wrap text to multiple lines
---@param text string
---@param width number
---@return table Array of lines
function Widget.WrapText(text, width)
    if not text or width <= 0 then return {} end

    local lines = {}
    local currentLine = {}
    local currentWidth = 0

    -- Split by words (simple space-based splitting)
    for word in text:gmatch("%S+") do
        local wordWidth = Widget.TextWidth(word)

        if currentWidth == 0 then
            -- First word on line
            currentLine[#currentLine + 1] = word
            currentWidth = wordWidth
        elseif currentWidth + 1 + wordWidth <= width then
            -- Word fits on current line
            currentLine[#currentLine + 1] = " "
            currentLine[#currentLine + 1] = word
            currentWidth = currentWidth + 1 + wordWidth
        else
            -- Word doesn't fit, start new line
            lines[#lines + 1] = table.concat(currentLine)
            currentLine = { word }
            currentWidth = wordWidth
        end
    end

    -- Add last line if any
    if #currentLine > 0 then
        lines[#lines + 1] = table.concat(currentLine)
    end

    return lines
end

---Split text by lines (handles \n)
---@param text string
---@return table Array of lines
function Widget.SplitLines(text)
    if not text then return {} end

    local lines = {}
    for line in text:gmatch("[^\r\n]*") do
        lines[#lines + 1] = line
    end
    return lines
end

---Create a horizontal divider string
---@param width number
---@param char? string Divider character (default "─")
---@return string
function Widget.Divider(width, char)
    char = char or "─"
    return string.rep(char, width)
end

---Get theme color helper
---@param key string Color key like "accent.primary"
---@return string|nil Hex color
function Widget.GetColor(key)
    return C3.Colors and C3.Colors.GetColor(key) or nil
end

---Apply theme color to text
---@param text string
---@param colorKey string Color key
---@return string Colorized text (with WoW codes)
function Widget.Colorize(text, colorKey)
    if not text then return "" end
    local hex = Widget.GetColor(colorKey)
    if hex then
        return "|cFF" .. hex .. text .. "|r"
    end
    return text
end

-------------------------------------------------------------------------------
-- Block Container Helper
-------------------------------------------------------------------------------

---Render content within an optional block container
---Handles the common pattern of rendering a block and getting inner rect.
---@param widget table Widget with optional _block property
---@param buf table TuiBuffer
---@param rect table Rect
---@param renderFn function(innerRect) Function to render content in inner area
function Widget.RenderWithBlock(widget, buf, rect, renderFn)
    local innerRect = rect
    local releaseInner = false

    if widget._block then
        widget._block:render(buf, rect)
        innerRect = widget._block:inner(rect)
        releaseInner = true
    end

    if innerRect.width > 0 and innerRect.height > 0 then
        renderFn(innerRect)
    end

    if releaseInner then
        C3.Rect.Release(innerRect)
    end
end

return Widget
