--[[
    Commander 3.0 - Rich Components

    Renders actual WoW frames for rich output:
    - Icon grids (items, mounts, spells)
    - Progress bars
    - Clickable buttons

    Components are rendered as overlays on the output display.
]]

local ADDON_NAME, C3 = ...

-------------------------------------------------------------------------------
-- Rich Components Module
-------------------------------------------------------------------------------

local RichComponents = {}
C3.UI = C3.UI or {}
C3.UI.RichComponents = RichComponents

-- Active component frames (indexed by line ID)
RichComponents._frames = {}

-- Component type registry
RichComponents._types = {}

-- Frame pools for reuse (reduces GC pressure)
RichComponents._framePools = {}

-------------------------------------------------------------------------------
-- Frame Pool Management
-------------------------------------------------------------------------------

---Get or create a frame pool for a component type
---@param componentType string
---@return table pool
local function getFramePool(componentType)
    if not RichComponents._framePools[componentType] then
        RichComponents._framePools[componentType] = {
            available = {},  -- Frames ready for reuse
            count = 0,       -- Total frames ever created
        }
    end
    return RichComponents._framePools[componentType]
end

---Acquire a frame from pool or create new
---@param componentType string
---@param parent frame
---@return frame
function RichComponents:AcquireFrame(componentType, parent)
    local pool = getFramePool(componentType)

    -- Try to reuse an available frame
    local frame = table.remove(pool.available)
    if frame then
        frame:SetParent(parent)
        frame:Show()
        return frame
    end

    -- Create new frame
    pool.count = pool.count + 1
    return CreateFrame("Frame", nil, parent)
end

---Release a frame back to the pool
---@param componentType string
---@param frame frame
function RichComponents:ReleaseFrame(componentType, frame)
    local pool = getFramePool(componentType)

    -- Reset frame state
    frame:Hide()
    frame:ClearAllPoints()
    frame:SetParent(nil)

    table.insert(pool.available, frame)
end

---Get pool statistics
---@return table stats
function RichComponents:GetPoolStats()
    local stats = {}
    for typeName, pool in pairs(self._framePools) do
        stats[typeName] = {
            total = pool.count,
            available = #pool.available,
            inUse = pool.count - #pool.available,
        }
    end
    return stats
end

-------------------------------------------------------------------------------
-- Component Registration
-------------------------------------------------------------------------------

---Register a component type
---@param typeName string
---@param definition table { create, update, destroy, height }
function RichComponents:Register(typeName, definition)
    self._types[typeName] = definition
end

---Get a component type definition
---@param typeName string
---@return table?
function RichComponents:Get(typeName)
    return self._types[typeName]
end

-------------------------------------------------------------------------------
-- Component Lifecycle
-------------------------------------------------------------------------------

---Create a component for a buffer line
---@param lineId number
---@param componentType string
---@param data table
---@param parent frame
---@return frame?
function RichComponents:Create(lineId, componentType, data, parent)
    local typeDef = self._types[componentType]
    if not typeDef then
        return nil
    end

    -- Destroy existing component at this line
    self:Destroy(lineId)

    -- Create new component
    local frame = typeDef.create(data, parent)
    if frame then
        frame._componentType = componentType
        frame._componentData = data
        frame._lineId = lineId
        self._frames[lineId] = frame
    end

    return frame
end

---Update a component's data
---@param lineId number
---@param data table
function RichComponents:Update(lineId, data)
    local frame = self._frames[lineId]
    if not frame then return end

    local typeDef = self._types[frame._componentType]
    if typeDef and typeDef.update then
        typeDef.update(frame, data)
        frame._componentData = data
    end
end

---Destroy a component
---@param lineId number
function RichComponents:Destroy(lineId)
    local frame = self._frames[lineId]
    if not frame then return end

    local typeDef = self._types[frame._componentType]
    if typeDef and typeDef.destroy then
        typeDef.destroy(frame)
    else
        frame:Hide()
        frame:SetParent(nil)
    end

    self._frames[lineId] = nil
end

---Destroy all components
function RichComponents:DestroyAll()
    for lineId in pairs(self._frames) do
        self:Destroy(lineId)
    end
end

---Get component height
---@param componentType string
---@param data table
---@return number
function RichComponents:GetHeight(componentType, data)
    local typeDef = self._types[componentType]
    if typeDef then
        if type(typeDef.height) == "function" then
            return typeDef.height(data)
        elseif type(typeDef.height) == "number" then
            return typeDef.height
        end
    end
    return 20  -- Default height
end

-------------------------------------------------------------------------------
-- Icon Grid Component
-------------------------------------------------------------------------------

local ICON_SIZE = 32
local ICON_PADDING = 4
local ICONS_PER_ROW = 8

RichComponents:Register("IconGrid", {
    height = function(data)
        local count = data.items and #data.items or 0
        local rows = math.ceil(count / ICONS_PER_ROW)
        return math.max(1, rows) * (ICON_SIZE + ICON_PADDING) + ICON_PADDING
    end,

    create = function(data, parent)
        -- Use frame pool for the container
        local frame = RichComponents:AcquireFrame("IconGrid", parent)
        frame:SetSize(parent:GetWidth() - 20, 1)  -- Height set dynamically

        -- Initialize or reuse icon pool
        frame._iconPool = frame._iconPool or {}
        frame._activeIcons = {}

        -- Create icon pool (only create if not already present from pool reuse)
        local maxIcons = ICONS_PER_ROW * 4  -- Max 4 rows
        for i = 1, maxIcons do
            if not frame._iconPool[i] then
                local icon = CreateFrame("Button", nil, frame)
                icon:SetSize(ICON_SIZE, ICON_SIZE)
                icon:Hide()

                -- Background (shows when no texture)
                icon.bg = icon:CreateTexture(nil, "BACKGROUND")
                icon.bg:SetAllPoints()
                icon.bg:SetColorTexture(0.1, 0.1, 0.1, 0.8)

                -- Icon texture
                icon.texture = icon:CreateTexture(nil, "ARTWORK")
                icon.texture:SetAllPoints()

                -- Border frame (not a solid texture!)
                icon.borderFrame = CreateFrame("Frame", nil, icon, "BackdropTemplate")
                icon.borderFrame:SetPoint("TOPLEFT", -1, 1)
                icon.borderFrame:SetPoint("BOTTOMRIGHT", 1, -1)
                icon.borderFrame:SetBackdrop({
                    edgeFile = "Interface\\Buttons\\WHITE8x8",
                    edgeSize = 1,
                })
                icon.borderFrame:SetBackdropBorderColor(0.3, 0.3, 0.3, 1)

                -- Highlight
                icon:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square", "ADD")

                -- Count text
                local font = C3.UI.Theme and C3.UI.Theme.Font or "Fonts\\FRIZQT__.TTF"
                icon.count = icon:CreateFontString(nil, "OVERLAY")
                icon.count:SetFont(font, 10, "")
                icon.count:SetPoint("BOTTOMRIGHT", -2, 2)
                icon.count:SetJustifyH("RIGHT")

                -- Quality border overlay
                icon.qualityBorder = icon:CreateTexture(nil, "OVERLAY", nil, 1)
                icon.qualityBorder:SetPoint("TOPLEFT", -2, 2)
                icon.qualityBorder:SetPoint("BOTTOMRIGHT", 2, -2)
                icon.qualityBorder:SetTexture("Interface\\Common\\WhiteIconFrame")
                icon.qualityBorder:Hide()

                frame._iconPool[i] = icon
            else
                -- Ensure pooled icons have correct parent
                frame._iconPool[i]:SetParent(frame)
            end
        end

        -- Populate with data
        RichComponents._types.IconGrid.update(frame, data)

        return frame
    end,

    update = function(frame, data)
        local items = data.items or {}

        -- Hide all icons first
        for _, icon in ipairs(frame._activeIcons) do
            icon:Hide()
            icon:SetScript("OnEnter", nil)
            icon:SetScript("OnLeave", nil)
            icon:SetScript("OnClick", nil)
        end
        frame._activeIcons = {}

        -- Position and show icons
        local x, y = ICON_PADDING, -ICON_PADDING
        local maxWidth = frame:GetWidth()
        local col = 0

        for i, item in ipairs(items) do
            if i > #frame._iconPool then break end

            local icon = frame._iconPool[i]
            icon:ClearAllPoints()
            icon:SetPoint("TOPLEFT", x, y)
            icon:Show()

            -- Set texture (handle nil, 0, or invalid icons)
            local textureSet = false
            if item.icon and item.icon ~= 0 then
                icon.texture:SetTexture(item.icon)
                textureSet = true
            end
            if not textureSet and item.itemId then
                local itemIcon = C_Item.GetItemIconByID(item.itemId)
                if itemIcon and itemIcon ~= 0 then
                    icon.texture:SetTexture(itemIcon)
                    textureSet = true
                end
            end
            if not textureSet then
                icon.texture:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
            end

            -- Set count
            if item.count and item.count > 1 then
                icon.count:SetText(item.count)
                icon.count:Show()
            else
                icon.count:Hide()
            end

            -- Set quality border
            if item.quality and item.quality > 1 then
                local r, g, b = C_Item.GetItemQualityColor(item.quality)
                icon.qualityBorder:SetVertexColor(r, g, b, 1)
                icon.qualityBorder:Show()
            else
                icon.qualityBorder:Hide()
            end

            -- Tooltip
            icon:SetScript("OnEnter", function(self)
                GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
                if item.itemLink then
                    GameTooltip:SetHyperlink(item.itemLink)
                elseif item.itemId then
                    GameTooltip:SetItemByID(item.itemId)
                elseif item.spellId then
                    GameTooltip:SetSpellByID(item.spellId)
                elseif item.mountId then
                    local name, spellID = C_MountJournal.GetMountInfoByID(item.mountId)
                    if spellID then
                        GameTooltip:SetSpellByID(spellID)
                    end
                elseif item.name then
                    GameTooltip:AddLine(item.name)
                end
                GameTooltip:Show()
            end)

            icon:SetScript("OnLeave", function()
                GameTooltip:Hide()
            end)

            -- Click handler
            if item.onClick then
                icon:SetScript("OnClick", item.onClick)
            elseif item.command then
                icon:SetScript("OnClick", function()
                    C3:Execute(item.command)
                end)
            end

            table.insert(frame._activeIcons, icon)

            -- Next position
            col = col + 1
            x = x + ICON_SIZE + ICON_PADDING
            if col >= ICONS_PER_ROW or x + ICON_SIZE > maxWidth then
                col = 0
                x = ICON_PADDING
                y = y - (ICON_SIZE + ICON_PADDING)
            end
        end

        -- Update frame height
        local rows = math.ceil(#items / ICONS_PER_ROW)
        frame:SetHeight(math.max(1, rows) * (ICON_SIZE + ICON_PADDING) + ICON_PADDING)
    end,

    destroy = function(frame)
        -- Hide all icons but keep them attached for reuse
        for _, icon in ipairs(frame._iconPool) do
            icon:Hide()
            icon:SetScript("OnEnter", nil)
            icon:SetScript("OnLeave", nil)
            icon:SetScript("OnClick", nil)
        end
        frame._activeIcons = {}

        -- Release frame back to pool for reuse
        RichComponents:ReleaseFrame("IconGrid", frame)
    end,
})

-------------------------------------------------------------------------------
-- Progress Bar Component (Actual StatusBar)
-------------------------------------------------------------------------------

RichComponents:Register("ProgressBar", {
    height = 20,

    create = function(data, parent)
        -- Use frame pool - ProgressBar needs StatusBar so we handle it specially
        local pool = getFramePool("ProgressBar")
        local frame = table.remove(pool.available)
        if frame then
            frame:SetParent(parent)
            frame:Show()
        else
            pool.count = pool.count + 1
            frame = CreateFrame("StatusBar", nil, parent)

            -- Only create sub-elements once
            -- Background
            frame.bg = frame:CreateTexture(nil, "BACKGROUND")
            frame.bg:SetAllPoints()
            frame.bg:SetColorTexture(0.1, 0.1, 0.1, 0.8)

            -- Border
            frame.border = CreateFrame("Frame", nil, frame, "BackdropTemplate")
            frame.border:SetPoint("TOPLEFT", -1, 1)
            frame.border:SetPoint("BOTTOMRIGHT", 1, -1)
            frame.border:SetBackdrop({
                edgeFile = "Interface\\Buttons\\WHITE8x8",
                edgeSize = 1,
            })
            frame.border:SetBackdropBorderColor(0.3, 0.3, 0.3, 1)

            -- Label
            local font = C3.UI.Theme and C3.UI.Theme.Font or "Fonts\\FRIZQT__.TTF"
            frame.label = frame:CreateFontString(nil, "OVERLAY")
            frame.label:SetFont(font, 10, "")
            frame.label:SetPoint("LEFT", 4, 0)

            -- Value text
            frame.valueText = frame:CreateFontString(nil, "OVERLAY")
            frame.valueText:SetFont(font, 10, "")
            frame.valueText:SetPoint("RIGHT", -4, 0)
        end
        frame:SetSize(200, 16)
        frame:SetStatusBarTexture("Interface\\TargetingFrame\\UI-StatusBar")
        frame:SetMinMaxValues(0, data.max or 100)
        frame:SetValue(data.current or 0)
        frame.label:SetText(data.label or "")

        RichComponents._types.ProgressBar.update(frame, data)

        return frame
    end,

    update = function(frame, data)
        local current = data.current or 0
        local max = data.max or 100
        local percent = max > 0 and (current / max * 100) or 0

        frame:SetMinMaxValues(0, max)
        frame:SetValue(current)

        -- Color by percentage
        local r, g, b
        if percent >= 75 then
            r, g, b = 0, 0.8, 0
        elseif percent >= 25 then
            r, g, b = 0.8, 0.8, 0
        else
            r, g, b = 0.8, 0, 0
        end
        frame:SetStatusBarColor(r, g, b)

        -- Update text
        if data.label then
            frame.label:SetText(data.label)
        end

        if data.showValues then
            frame.valueText:SetText(string.format("%d/%d", current, max))
        else
            frame.valueText:SetText(string.format("%d%%", math.floor(percent)))
        end
    end,

    destroy = function(frame)
        -- Release back to pool
        RichComponents:ReleaseFrame("ProgressBar", frame)
    end,
})

-------------------------------------------------------------------------------
-- Button Row Component
-------------------------------------------------------------------------------

RichComponents:Register("ButtonRow", {
    height = 28,

    create = function(data, parent)
        local frame = RichComponents:AcquireFrame("ButtonRow", parent)
        frame:SetSize(parent:GetWidth() - 20, 24)

        -- Initialize or reuse button pool
        frame._buttons = frame._buttons or {}
        frame._activeButtonCount = 0

        local x = 0
        for i, btnData in ipairs(data.buttons or {}) do
            local btn = frame._buttons[i]
            if not btn then
                btn = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
                frame._buttons[i] = btn
            else
                btn:SetParent(frame)
            end

            btn:SetSize(btnData.width or 80, 22)
            btn:ClearAllPoints()
            btn:SetPoint("LEFT", x, 0)
            btn:SetText(btnData.text or "Button")
            btn:Show()

            if btnData.command then
                btn:SetScript("OnClick", function()
                    C3:Execute(btnData.command)
                end)
            elseif btnData.onClick then
                btn:SetScript("OnClick", btnData.onClick)
            else
                btn:SetScript("OnClick", nil)
            end

            frame._activeButtonCount = i
            x = x + (btnData.width or 80) + 8
        end

        -- Hide unused buttons
        for i = frame._activeButtonCount + 1, #frame._buttons do
            frame._buttons[i]:Hide()
            frame._buttons[i]:SetScript("OnClick", nil)
        end

        return frame
    end,

    update = function(frame, data)
        -- Buttons are static, no update needed
    end,

    destroy = function(frame)
        for _, btn in ipairs(frame._buttons) do
            btn:Hide()
            btn:SetScript("OnClick", nil)
        end
        frame._activeButtonCount = 0

        RichComponents:ReleaseFrame("ButtonRow", frame)
    end,
})

-------------------------------------------------------------------------------
-- Secret Value Indicator (WoW 12.0+ compatibility)
-------------------------------------------------------------------------------

---Get a formatted "???" indicator for restricted values
---Used when combat data is unavailable due to WoW 12.0's secret values system
---@return string Colorized "???" text
function RichComponents.SecretValueIndicator()
    -- Use theme color if available, fallback to dim gray
    local dimColor = "4C566A"
    if C3.Colors and C3.Colors.GetColor then
        dimColor = C3.Colors.GetColor("status.dim") or dimColor
    end
    return string.format("|cFF%s???|r", dimColor)
end

---Format a value, showing "???" if nil (for secret values)
---@param value any The value to format
---@param formatStr? string Optional format string (default: "%s")
---@return string Formatted value or "???"
function RichComponents.FormatOrSecret(value, formatStr)
    if value == nil then
        return RichComponents.SecretValueIndicator()
    end
    return string.format(formatStr or "%s", value)
end

return RichComponents
