--[[
    Commander 3.0 - TUI List Widget

    A scrollable list of items with selection highlighting.
    Stateless - receives items and state (selected index, scroll offset) as props.

    Usage:
        local state = ListState:new()
        local list = List:new()
            :items({ "Item 1", "Item 2", "Item 3" })
            :block(Block:new():borders(Block.ALL):title("Menu"))
            :highlightColor("88C0D0")

        list:render(buf, rect, state)

        -- Navigation
        state:next(#items)
        state:prev()
]]

local ADDON_NAME, C3 = ...

local Widget = C3.TUI.Widget
local Symbols = C3.TUI.Symbols

-------------------------------------------------------------------------------
-- ListState - Tracks selection and scroll position
-------------------------------------------------------------------------------

local ListState = C3.class()
C3.TUI.ListState = ListState

function ListState:init()
    self.selected = 1      -- 1-indexed selected item
    self.offset = 0        -- Scroll offset (0-indexed)
end

---Select a specific index
---@param index number
function ListState:select(index)
    self.selected = math.max(1, index)
end

---Move selection down
---@param itemCount number Total number of items
function ListState:next(itemCount)
    if self.selected < itemCount then
        self.selected = self.selected + 1
    end
end

---Move selection up
function ListState:prev()
    if self.selected > 1 then
        self.selected = self.selected - 1
    end
end

---Jump to first item
function ListState:first()
    self.selected = 1
    self.offset = 0
end

---Jump to last item
---@param itemCount number
---@param visibleCount number
function ListState:last(itemCount, visibleCount)
    self.selected = itemCount
    self.offset = math.max(0, itemCount - visibleCount)
end

---Page down
---@param itemCount number
---@param visibleCount number
function ListState:pageDown(itemCount, visibleCount)
    self.selected = math.min(self.selected + visibleCount, itemCount)
end

---Page up
---@param visibleCount number
function ListState:pageUp(visibleCount)
    self.selected = math.max(self.selected - visibleCount, 1)
end

---Reset state
function ListState:reset()
    self.selected = 1
    self.offset = 0
end

-------------------------------------------------------------------------------
-- List Widget
-------------------------------------------------------------------------------

local List = C3.class()
C3.TUI.Widgets.List = List

function List:init()
    self._items = {}
    self._block = nil
    self._highlightSymbol = Symbols.MARKERS.pointer .. " "
    self._highlightColor = nil   -- Resolved at render time via TUI.Colors
    self._itemColor = nil        -- Resolved at render time via TUI.Colors
    self._dimColor = nil         -- Resolved at render time via TUI.Colors
    self._showScrollIndicator = true
end

---Set items
---Items can be strings or tables with { text, color?, icon?, data? }
---@param items table Array of items
---@return List
function List:items(items)
    self._items = items or {}
    return self
end

---Set containing block (optional border/title)
---@param block table Block widget
---@return List
function List:block(block)
    self._block = block
    return self
end

---Set highlight symbol (shown before selected item)
---@param symbol string
---@return List
function List:highlightSymbol(symbol)
    self._highlightSymbol = symbol
    return self
end

---Set highlight color for selected item
---@param color string Hex color
---@return List
function List:highlightColor(color)
    self._highlightColor = color
    return self
end

---Set default item color
---@param color string Hex color
---@return List
function List:itemColor(color)
    self._itemColor = color
    return self
end

---Set dim color for unselected items
---@param color string Hex color
---@return List
function List:dimColor(color)
    self._dimColor = color
    return self
end

---Show/hide scroll indicator
---@param show boolean
---@return List
function List:showScrollIndicator(show)
    self._showScrollIndicator = show
    return self
end

---Get item text
---@param item any String or table with text property
---@return string
local function getItemText(item)
    if type(item) == "string" then
        return item
    elseif type(item) == "table" then
        return item.text or item[1] or ""
    end
    return tostring(item)
end

---Get item color
---@param item any
---@param defaultColor string|nil
---@return string|nil
local function getItemColor(item, defaultColor)
    if type(item) == "table" and item.color then
        return item.color
    end
    return defaultColor
end

---Render the list
---@param buf table TuiBuffer
---@param rect table Rect
---@param state? table ListState
function List:render(buf, rect, state)
    state = state or ListState:new()

    local TuiColors = C3.TUI.Colors

    -- Resolve colors (use explicit or fall back to theme)
    local highlightColor = TuiColors.Resolve(self._highlightColor, TuiColors.highlight)
    local itemColor = TuiColors.Resolve(self._itemColor, TuiColors.text)
    local dimColor = TuiColors.Resolve(self._dimColor, TuiColors.textDim)

    local list = self
    Widget.RenderWithBlock(self, buf, rect, function(innerRect)
        local visibleCount = innerRect.height
        local itemCount = #list._items
        local symbolWidth = Widget.TextWidth(list._highlightSymbol)

        -- Adjust scroll offset to keep selection visible
        if state.selected <= state.offset then
            state.offset = state.selected - 1
        elseif state.selected > state.offset + visibleCount then
            state.offset = state.selected - visibleCount
        end
        state.offset = math.max(0, math.min(state.offset, math.max(0, itemCount - visibleCount)))

        -- Render visible items
        for i = 1, visibleCount do
            local itemIndex = state.offset + i
            if itemIndex > itemCount then break end

            local item = list._items[itemIndex]
            local text = getItemText(item)
            local y = innerRect.y + i - 1
            local x = innerRect.x
            local maxTextWidth = innerRect.width - symbolWidth

            if itemIndex == state.selected then
                -- Selected item: show highlight symbol and color
                buf:SetString(x, y, list._highlightSymbol, highlightColor)
                local displayText = Widget.Truncate(text, maxTextWidth)
                buf:SetString(x + symbolWidth, y, displayText, highlightColor or getItemColor(item, itemColor))
            else
                -- Normal item: pad where symbol would be, use normal/dim color
                buf:SetString(x, y, string.rep(" ", symbolWidth), nil)
                local displayText = Widget.Truncate(text, maxTextWidth)
                local color = getItemColor(item, dimColor or itemColor)
                buf:SetString(x + symbolWidth, y, displayText, color)
            end
        end

        -- Scroll indicators
        if list._showScrollIndicator and itemCount > visibleCount then
            local indicatorX = innerRect.x + innerRect.width - 1

            -- Up indicator
            if state.offset > 0 then
                buf:SetCell(indicatorX, innerRect.y, Symbols.SCROLLBAR.up, dimColor)
            end

            -- Down indicator
            if state.offset + visibleCount < itemCount then
                buf:SetCell(indicatorX, innerRect.y + visibleCount - 1, Symbols.SCROLLBAR.down, dimColor)
            end
        end
    end)
end

---Get the currently selected item
---@param state table ListState
---@return any|nil
function List:getSelectedItem(state)
    if state.selected >= 1 and state.selected <= #self._items then
        return self._items[state.selected]
    end
    return nil
end

---Get item count
---@return number
function List:getItemCount()
    return #self._items
end

return List
