local NS = rawget(_G, "MCDVoiceCooldownNS") or {}
_G.MCDVoiceCooldownNS = NS

NS.UI = NS.UI or {}
NS.UI.SavedListRenderer = NS.UI.SavedListRenderer or {}

local Renderer = NS.UI.SavedListRenderer
local SavedListRowFactory = NS.UI.SavedListRowFactory or {}
local SavedListRowRenderer = NS.UI.SavedListRowRenderer or {}
local SavedListHeaders = NS.UI.SavedListHeaders or {}
local L = NS.L or function(key, ...) if select("#", ...) > 0 then return string.format(tostring(key), ...) end return tostring(key) end

local ROW_HEIGHT = 28
local GROUP_ROW_HEIGHT = 30
local EMPTY_ROW_HEIGHT = 24
local HEADER_HEIGHT = 32

local function EnsureRow(list, index)
    if SavedListRowFactory and type(SavedListRowFactory.EnsureRow) == "function" then
        return SavedListRowFactory:EnsureRow(list, index)
    end
    list.rows = list.rows or {}
    if list.rows[index] then
        return list.rows[index]
    end
    local row = CreateFrame("Button", nil, list.content)
    row:SetHeight(ROW_HEIGHT)
    row:SetPoint("LEFT", list.content, "LEFT", 0, 0)
    row:SetPoint("RIGHT", list.content, "RIGHT", -4, 0)
    row.main = row:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    row.main:SetPoint("LEFT", row, "LEFT", 12, 0)
    row.main:SetPoint("RIGHT", row, "RIGHT", -12, 0)
    row.text = row.main
    row.bg = row:CreateTexture(nil, "BACKGROUND")
    row.bg:SetAllPoints(row)
    row.accent = row:CreateTexture(nil, "ARTWORK")
    row.icon = row:CreateTexture(nil, "ARTWORK")
    row.statusDot = row:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    list.rows[index] = row
    return row
end

local function EnsureHeader(list, index)
    if SavedListHeaders and type(SavedListHeaders.EnsureHeader) == "function" then
        return SavedListHeaders:EnsureHeader(list, index)
    end

    -- Minimal fallback for defensive loading; the normal path is UI/SavedListHeaders.lua.
    list.headers = list.headers or {}
    if list.headers[index] then
        return list.headers[index]
    end
    local header = CreateFrame("Button", nil, list.content)
    header:SetHeight(HEADER_HEIGHT)
    header:SetPoint("LEFT", list.content, "LEFT", 0, 0)
    header:SetPoint("RIGHT", list.content, "RIGHT", -4, 0)
    header.label = header:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    header.label:SetPoint("LEFT", header, "LEFT", 8, 0)
    header.arrow = header:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    header.arrow:SetPoint("RIGHT", header, "RIGHT", -8, 0)
    list.headers[index] = header
    return header
end


function Renderer:SelectKey(list, key, entryType)
    if SavedListRowFactory and type(SavedListRowFactory.SelectKey) == "function" then
        return SavedListRowFactory:SelectKey(list, key, entryType)
    end
end

local function RenderEntryRows(list, state, selectedKey, loadedEntries, unloadedEntries, loadedDisplayCount, unloadedDisplayCount)
    local yOffset = 0
    local rowIndex = 1
    local headerIndex = 1
    local selectedExists = false

    local function placeFrame(frame, height)
        frame:ClearAllPoints()
        frame:SetPoint("TOPLEFT", list.content, "TOPLEFT", 0, -yOffset)
        frame:SetPoint("RIGHT", list.content, "RIGHT", -4, 0)
        frame:Show()
        yOffset = yOffset + height
    end

    local function updateHeader(sectionKey, title, count)
        local header = EnsureHeader(list, headerIndex)
        header.sectionKey = sectionKey
        local collapsed = sectionKey == "loaded" and state.loadedCollapsed or state.unloadedCollapsed
        if header.label then
            header.label:SetText(string.format("%s（%d）", title, count))
        end
        if header.arrow then
            header.arrow:SetText(collapsed and "+" or "-")
        end
        placeFrame(header, HEADER_HEIGHT)
        headerIndex = headerIndex + 1
    end

    local function updateRow(entry, includeScopeText, isPlaceholder, canDrag, canDrop)
        local row = EnsureRow(list, rowIndex)
        local selected, height = false, ROW_HEIGHT
        if SavedListRowRenderer and type(SavedListRowRenderer.UpdateRow) == "function" then
            selected, height = SavedListRowRenderer:UpdateRow(row, entry, {
                includeScopeText = includeScopeText,
                isPlaceholder = isPlaceholder,
                canDrag = canDrag,
                canDrop = canDrop,
                selectedKey = selectedKey,
            })
        end
        if selected then
            selectedExists = true
        end
        placeFrame(row, height or ROW_HEIGHT)
        rowIndex = rowIndex + 1
    end

    updateHeader("loaded", L("HEADER_LOADED"), loadedDisplayCount)
    if not state.loadedCollapsed then
        if #loadedEntries == 0 then
            updateRow({
                key = "__empty_loaded",
                itemType = "entry",
                spellName = L("EMPTY_CONFIG"),
                index = 0,
                spellId = 0,
                modeText = "",
                icon = 134400,
            }, false, true, false)
        else
            for _, entry in ipairs(loadedEntries) do
                updateRow(entry, false, false, true, true)
            end
        end
    end

    updateHeader("unloaded", L("HEADER_UNLOADED"), unloadedDisplayCount)
    if not state.unloadedCollapsed then
        if #unloadedEntries == 0 then
            updateRow({
                key = "__empty_unloaded",
                itemType = "entry",
                spellName = L("EMPTY_CONFIG"),
                index = 0,
                spellId = 0,
                modeText = "",
                icon = 134400,
            }, false, true, false)
        else
            for _, entry in ipairs(unloadedEntries) do
                updateRow(entry, true, false, true, true)
            end
        end
    end

    for index = rowIndex, #list.rows do
        list.rows[index]:Hide()
    end
    for index = headerIndex, #list.headers do
        list.headers[index]:Hide()
    end

    if selectedKey ~= "" and not selectedExists then
        list.selectedKey = nil
    end

    local viewportHeight = list.frame:GetHeight()
    local contentHeight = math.max(yOffset, viewportHeight)
    list.content:SetHeight(contentHeight)
    local maxScroll = math.max(yOffset - viewportHeight, 0)

    if maxScroll > 0 then
        list.slider:Show()
        list.slider:SetMinMaxValues(0, maxScroll)
        if (list.slider:GetValue() or 0) > maxScroll then
            list.slider:SetValue(maxScroll)
        end
    else
        list.slider:SetMinMaxValues(0, 0)
        list.slider:SetValue(0)
        list.slider:Hide()
        list.scrollFrame:SetVerticalScroll(0)
    end

    return true
end


function Renderer:RenderEntryRows(list, state, selectedKey, loadedEntries, unloadedEntries, loadedDisplayCount, unloadedDisplayCount)
    return RenderEntryRows(list, state, selectedKey, loadedEntries, unloadedEntries, loadedDisplayCount, unloadedDisplayCount)
end
