--[[
    Commander 3.0 - TUI Block Widget

    Block is a container with borders, title, and padding.
    It's the foundational widget that wraps other widgets.

    Usage:
        local block = Block:new()
            :borders(Block.ALL)
            :borderType(Symbols.ROUNDED)
            :title("My Title")
            :titleColor("88C0D0")

        block:render(buf, rect)
        local inner = block:inner(rect)  -- Get content area

    Stateless: Block doesn't store data, just rendering config.
]]

local ADDON_NAME, C3 = ...

local Symbols = C3.TUI.Symbols

-------------------------------------------------------------------------------
-- Block Widget
-------------------------------------------------------------------------------

local Block = C3.class()
C3.TUI.Widgets = C3.TUI.Widgets or {}
C3.TUI.Widgets.Block = Block

-- Border flags (bit flags for sides)
Block.NONE = 0
Block.TOP = 1
Block.RIGHT = 2
Block.BOTTOM = 4
Block.LEFT = 8
Block.ALL = 15  -- TOP | RIGHT | BOTTOM | LEFT

---Create a new Block widget
function Block:init()
    self._borders = Block.NONE
    self._borderSet = Symbols.SINGLE
    self._borderColor = nil
    self._title = nil
    self._titleColor = nil
    self._titleAlign = "left"  -- "left", "center", "right"
    self._padding = { top = 0, right = 0, bottom = 0, left = 0 }
end

---Convenience constructor for bordered block
---@return Block
function Block.Bordered()
    return Block:new():borders(Block.ALL)
end

---Set which borders to draw
---@param flags number Border flags (Block.TOP | Block.LEFT, etc.)
---@return Block self (for chaining)
function Block:borders(flags)
    self._borders = flags
    return self
end

---Set border character set
---@param borderSet table|string Symbol set or name ("single", "rounded", etc.)
---@return Block
function Block:borderType(borderSet)
    if type(borderSet) == "string" then
        local resolved = Symbols.GetBorder(borderSet)
        if resolved then
            self._borderSet = resolved
        end
        -- Keep existing if invalid name (silent fallback)
    elseif type(borderSet) == "table" then
        -- Validate that the table has required border keys
        if borderSet.topLeft and borderSet.horizontal and borderSet.vertical then
            self._borderSet = borderSet
        end
        -- Keep existing if invalid table (silent fallback)
    end
    return self
end

---Set border color
---@param color string Hex color (e.g., "88C0D0")
---@return Block
function Block:borderColor(color)
    self._borderColor = color
    return self
end

---Set title
---@param title string
---@return Block
function Block:title(title)
    self._title = title
    return self
end

---Set title color
---@param color string Hex color
---@return Block
function Block:titleColor(color)
    self._titleColor = color
    return self
end

---Set title alignment
---@param align string "left", "center", or "right"
---@return Block
function Block:titleAlign(align)
    self._titleAlign = align
    return self
end

---Set padding (internal margin)
---@param padding number|table Single value or {top, right, bottom, left} or {vertical, horizontal}
---@return Block
function Block:padding(padding)
    if type(padding) == "number" then
        self._padding = { top = padding, right = padding, bottom = padding, left = padding }
    elseif type(padding) == "table" then
        if #padding == 1 then
            local p = padding[1]
            self._padding = { top = p, right = p, bottom = p, left = p }
        elseif #padding == 2 then
            -- {vertical, horizontal}
            self._padding = { top = padding[1], right = padding[2], bottom = padding[1], left = padding[2] }
        elseif #padding == 4 then
            self._padding = { top = padding[1], right = padding[2], bottom = padding[3], left = padding[4] }
        else
            -- Named table
            self._padding = {
                top = padding.top or 0,
                right = padding.right or 0,
                bottom = padding.bottom or 0,
                left = padding.left or 0,
            }
        end
    end
    return self
end

---Check if a border side is enabled
---@param side number Border flag (Block.TOP, etc.)
---@return boolean
function Block:hasBorder(side)
    return bit.band(self._borders, side) ~= 0
end

---Calculate the inner area (content area after borders and padding)
---@param rect table Rect
---@return table Rect (use Rect.Release when done)
function Block:inner(rect)
    local x = rect.x
    local y = rect.y
    local w = rect.width
    local h = rect.height

    -- Account for borders
    if self:hasBorder(Block.LEFT) then
        x = x + 1
        w = w - 1
    end
    if self:hasBorder(Block.RIGHT) then
        w = w - 1
    end
    if self:hasBorder(Block.TOP) then
        y = y + 1
        h = h - 1
    end
    if self:hasBorder(Block.BOTTOM) then
        h = h - 1
    end

    -- Account for padding
    x = x + self._padding.left
    y = y + self._padding.top
    w = w - self._padding.left - self._padding.right
    h = h - self._padding.top - self._padding.bottom

    return C3.Rect.Acquire(x, y, math.max(0, w), math.max(0, h))
end

---Render the block to a buffer
---@param buf table TuiBuffer
---@param rect table Rect
function Block:render(buf, rect)
    if rect.width <= 0 or rect.height <= 0 then return end

    local bs = self._borderSet
    local bc = self._borderColor

    local hasTop = self:hasBorder(Block.TOP)
    local hasBottom = self:hasBorder(Block.BOTTOM)
    local hasLeft = self:hasBorder(Block.LEFT)
    local hasRight = self:hasBorder(Block.RIGHT)

    local right = rect.x + rect.width - 1
    local bottom = rect.y + rect.height - 1

    -- Draw corners
    if hasTop and hasLeft then
        buf:SetCell(rect.x, rect.y, bs.topLeft, bc)
    end
    if hasTop and hasRight then
        buf:SetCell(right, rect.y, bs.topRight, bc)
    end
    if hasBottom and hasLeft then
        buf:SetCell(rect.x, bottom, bs.bottomLeft, bc)
    end
    if hasBottom and hasRight then
        buf:SetCell(right, bottom, bs.bottomRight, bc)
    end

    -- Draw horizontal borders (excluding corners)
    local hStart = rect.x + (hasLeft and 1 or 0)
    local hEnd = right - (hasRight and 1 or 0)

    if hasTop then
        for x = hStart, hEnd do
            buf:SetCell(x, rect.y, bs.horizontal, bc)
        end
    end
    if hasBottom then
        for x = hStart, hEnd do
            buf:SetCell(x, bottom, bs.horizontal, bc)
        end
    end

    -- Draw vertical borders (excluding corners)
    local vStart = rect.y + (hasTop and 1 or 0)
    local vEnd = bottom - (hasBottom and 1 or 0)

    if hasLeft then
        for y = vStart, vEnd do
            buf:SetCell(rect.x, y, bs.vertical, bc)
        end
    end
    if hasRight then
        for y = vStart, vEnd do
            buf:SetCell(right, y, bs.vertical, bc)
        end
    end

    -- Draw title (on top border)
    if self._title and hasTop then
        local title = " " .. self._title .. " "
        local titleLen = C3.TUI.Widget.TextWidth(title)
        local maxTitleWidth = rect.width - 4  -- Leave room for corners + padding
        local startX

        if titleLen > maxTitleWidth then
            title = C3.TUI.Widget.Truncate(title, maxTitleWidth)
            titleLen = maxTitleWidth
        end

        if self._titleAlign == "center" then
            startX = rect.x + math.floor((rect.width - titleLen) / 2)
        elseif self._titleAlign == "right" then
            startX = right - titleLen - (hasRight and 1 or 0)
        else  -- left (default)
            startX = rect.x + (hasLeft and 2 or 1)
        end

        startX = math.max(rect.x + 1, startX)
        buf:SetString(startX, rect.y, title, self._titleColor or bc)
    end
end

---Render block and return inner rect in one call
---@param buf table TuiBuffer
---@param rect table Rect
---@return table innerRect (use Rect.Release when done)
function Block:renderAndGetInner(buf, rect)
    self:render(buf, rect)
    return self:inner(rect)
end

return Block
