local addonName, ZL = ...
local LOGO_TEXTURE = "Interface\\AddOns\\ZoneLocked\\assets\\zonelocked-logo.png"

local CATEGORY_ICONS = {
    achievements = 236621,
    quests = 413581,
    mounts = 132261,
    pets = 656595,
    decor = 134435,
    instances = 136106,
    transmogs = 135048,
    rares = 132176,
}

local function StylePanel(frame)
    local theme = ZL:GetTheme()
    frame:SetBackdrop({
        bgFile = "Interface/FrameGeneral/UI-Background-Rock",
        edgeFile = "Interface/DialogFrame/UI-DialogBox-Border",
        tile = true,
        tileSize = 128,
        edgeSize = 24,
        insets = { left = 5, right = 5, top = 5, bottom = 5 },
    })
    frame:SetBackdropColor(unpack(theme.window))
    frame:SetBackdropBorderColor(unpack(theme.border))
end

function ZL:ApplyMiniListTheme()
    local frame, theme = self.MiniList, self:GetTheme()
    if not frame then return end
    frame:SetBackdropColor(unpack(theme.window))
    frame:SetBackdropBorderColor(unpack(theme.border))
    frame.header:SetColorTexture(unpack(theme.header))
    frame.headerAccent:SetColorTexture(unpack(theme.accent))
    frame.title:SetTextColor(unpack(theme.accent))
    frame.kicker:SetTextColor(unpack(theme.secondary))
    frame.categoriesTitle:SetTextColor(unpack(theme.accent))
    frame.missingTitle:SetTextColor(unpack(theme.accent))
    frame.progressTrack:SetBackdropColor(theme.panel[1], theme.panel[2], theme.panel[3], 1)
    frame.progressTrack:SetBackdropBorderColor(unpack(theme.secondary))
end

local function StyleRow(frame, r, g, b, a)
    frame:SetBackdrop({
        bgFile = "Interface/Buttons/WHITE8X8",
        edgeFile = "Interface/Tooltips/UI-Tooltip-Border",
        edgeSize = 8,
    })
    frame:SetBackdropColor(r, g, b, a)
    frame:SetBackdropBorderColor(0.2, 0.34, 0.42, 0.65)
end

local function SetBar(bar, done, total)
    local percent = total > 0 and math.floor((done / total) * 100 + 0.5) or 100
    bar:SetMinMaxValues(0, math.max(total, 1))
    bar:SetValue(done)
    bar.text:SetText(string.format("%d%%  |cffb9c7cf%d / %d|r", percent, done, total))
    local theme = ZL:GetTheme()
    local color = done == total and theme.secondary or theme.accent
    bar:SetStatusBarColor(color[1], color[2], color[3], color[4] or 0.96)
end

function ZL:CreateMiniList()
    local saved = ZL_Saved.ui.minilist
    local frame = CreateFrame("Frame", "ZoneLockedMiniList", UIParent, "BackdropTemplate")
    frame:SetSize(saved.compact and (saved.compactWidth or 300) or (saved.width or 370),
        saved.compact and (saved.compactHeight or 92) or (saved.height or 540))
    frame:SetResizable(true)
    frame:SetResizeBounds(220, 88, 650, saved.compact and 180 or 800)
    frame:SetClipsChildren(true)
    frame:SetPoint(saved.point or "RIGHT", UIParent, saved.relativePoint or "RIGHT", saved.x or -40, saved.y or 0)
    frame:SetClampedToScreen(true)
    frame:SetMovable(true)
    frame:EnableMouse(true)
    StylePanel(frame)

    frame.header = frame:CreateTexture(nil, "BACKGROUND")
    frame.header:SetPoint("TOPLEFT", 8, -8)
    frame.header:SetPoint("TOPRIGHT", -8, -8)
    frame.header:SetHeight(42)
    frame.header:SetColorTexture(0.035, 0.105, 0.145, 0.98)
    frame.headerAccent = frame:CreateTexture(nil, "ARTWORK")
    frame.headerAccent:SetPoint("BOTTOMLEFT", frame.header)
    frame.headerAccent:SetPoint("BOTTOMRIGHT", frame.header)
    frame.headerAccent:SetHeight(2)
    frame.headerAccent:SetColorTexture(0.85, 0.66, 0.25, 0.9)

    frame.logo = frame:CreateTexture(nil, "ARTWORK")
    frame.logo:SetTexture(LOGO_TEXTURE)
    frame.logo:SetTexCoord(0.16, 0.84, 0.02, 0.96)
    frame.logo:SetSize(38, 38)
    frame.logo:SetPoint("TOPLEFT", 10, -10)
    frame.title = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    frame.title:SetPoint("TOPLEFT", frame.logo, "TOPRIGHT", 7, -2)
    frame.title:SetJustifyH("LEFT")
    frame.kicker = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    frame.kicker:SetPoint("TOPLEFT", frame.title, "BOTTOMLEFT", 0, -2)
    frame.kicker:SetText("ZONE LOCKED PROGRESS")
    frame.kicker:SetTextColor(0.54, 0.78, 0.90)

    frame.close = CreateFrame("Button", nil, frame, "UIPanelCloseButton")
    frame.close:SetPoint("TOPRIGHT", 1, 1)
    frame.close:SetScript("OnClick", function()
        ZL_Saved.settings.minilist.enabled = false
        frame:Hide()
        if ZL.UI then ZL:UpdateToolbar() end
    end)
    frame.open = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
    frame.open:SetSize(48, 20)
    frame.open:SetPoint("RIGHT", frame.close, "LEFT", -1, 0)
    frame.open:SetText("Open")
    frame.open:SetScript("OnClick", function()
        if not self.UI:IsShown() then self.UI:Show() end
        self._uiSignature = nil
        self:RefreshUI()
    end)
    frame.collapse = CreateFrame("Button", nil, frame)
    frame.collapse:SetSize(22, 22)
    frame.collapse:SetPoint("RIGHT", frame.close, "LEFT", -1, 0)
    frame.collapse:SetNormalTexture(saved.compact and
        "Interface/Buttons/UI-Panel-CollapseButton-Up" or "Interface/Buttons/UI-Panel-HideButton-Up")
    frame.collapse:SetPushedTexture(saved.compact and
        "Interface/Buttons/UI-Panel-CollapseButton-Up" or "Interface/Buttons/UI-Panel-HideButton-Down")
    frame.collapse:SetHighlightTexture("Interface/Buttons/UI-Panel-MinimizeButton-Highlight", "ADD")
    frame.collapse:SetScript("OnEnter", function(button)
        GameTooltip:SetOwner(button, "ANCHOR_TOP")
        GameTooltip:SetText(ZL_Saved.ui.minilist.compact and "Expand Mini-List" or "Compact Mini-List")
        GameTooltip:AddLine(ZL_Saved.ui.minilist.compact and "Show category details." or "Keep only zone progress visible.",
            1, 1, 1, true)
        GameTooltip:Show()
    end)
    frame.collapse:SetScript("OnLeave", function(button)
        GameTooltip_Hide()
    end)
    frame.open:ClearAllPoints()
    frame.open:SetPoint("RIGHT", frame.collapse, "LEFT", -1, 0)
    frame.title:SetPoint("RIGHT", frame.open, "LEFT", -8, 2)

    -- An explicit drag handle works even when the rest of the list contains buttons.
    frame.dragHandle = CreateFrame("Button", nil, frame)
    frame.dragHandle:SetPoint("TOPLEFT", 8, -8)
    frame.dragHandle:SetPoint("BOTTOMRIGHT", frame.open, "BOTTOMLEFT", -4, -4)
    frame.dragHandle:RegisterForDrag("LeftButton")
    frame.dragHandle:SetScript("OnDragStart", function() frame:StartMoving() end)
    frame.dragHandle:SetScript("OnDragStop", function()
        frame:StopMovingOrSizing()
        local point, _, relativePoint, x, y = frame:GetPoint(1)
        local dimensions = ZL_Saved.ui.minilist
        dimensions.point, dimensions.relativePoint, dimensions.x, dimensions.y = point, relativePoint, x, y
        if dimensions.compact then
            dimensions.compactWidth, dimensions.compactHeight = frame:GetWidth(), frame:GetHeight()
        else
            dimensions.width, dimensions.height = frame:GetWidth(), frame:GetHeight()
        end
    end)
    frame.dragHandle:SetScript("OnEnter", function(handle)
        GameTooltip:SetOwner(handle, "ANCHOR_TOP")
        GameTooltip:SetText("Drag to move")
        GameTooltip:AddLine("Position is saved between sessions.", 1, 1, 1)
        GameTooltip:Show()
    end)
    frame.dragHandle:SetScript("OnLeave", GameTooltip_Hide)

    frame.progressTrack = CreateFrame("Frame", nil, frame, "BackdropTemplate")
    frame.progressTrack:SetPoint("TOPLEFT", 8, -57)
    frame.progressTrack:SetPoint("TOPRIGHT", -8, -57)
    frame.progressTrack:SetHeight(22)
    frame.progressTrack:SetBackdrop({
        bgFile = "Interface/Buttons/WHITE8X8",
        edgeFile = "Interface/Tooltips/UI-Tooltip-Border",
        edgeSize = 7,
    })
    frame.progressTrack:SetBackdropColor(0.035, 0.07, 0.085, 1)
    frame.progressTrack:SetBackdropBorderColor(0.20, 0.40, 0.48, 0.9)
    frame.progress = CreateFrame("StatusBar", nil, frame.progressTrack)
    frame.progress:SetStatusBarTexture("Interface/TargetingFrame/UI-StatusBar")
    frame.progress:SetPoint("TOPLEFT", 2, -2)
    frame.progress:SetPoint("BOTTOMRIGHT", -2, 2)
    frame.progressBG = frame.progress:CreateTexture(nil, "BACKGROUND")
    frame.progressBG:SetAllPoints()
    frame.progressBG:SetColorTexture(0.025, 0.03, 0.035, 0.95)
    frame.progress.text = frame.progress:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    frame.progress.text:SetPoint("CENTER")

    frame.categoriesTitle = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    frame.categoriesTitle:SetPoint("TOPLEFT", 15, -86)
    frame.categoriesTitle:SetText("COLLECTION CATEGORIES")
    frame.categoriesTitle:SetTextColor(0.95, 0.74, 0.32)

    frame.categories = {}
    for index = 1, 8 do
        local row = CreateFrame("Button", nil, frame, "BackdropTemplate")
        row:SetPoint("TOPLEFT", 15, -101 - ((index - 1) * 30))
        row:SetPoint("TOPRIGHT", -15, -101 - ((index - 1) * 30))
        row:SetHeight(29)
        StyleRow(row, 0.025, 0.045, 0.055, 0.88)
        row.branch = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
        row.branch:SetPoint("LEFT", 7, 0)
        row.branch:SetText("-")
        row.branch:SetTextColor(0.48, 0.62, 0.70)
        row.icon = row:CreateTexture(nil, "ARTWORK")
        row.icon:SetSize(19, 19)
        row.icon:SetPoint("LEFT", 20, 0)
        row.name = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
        row.name:SetPoint("LEFT", row.icon, "RIGHT", 7, 0)
        row.name:SetJustifyH("LEFT")
        row.progress = row:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        row.progress:SetPoint("RIGHT", -8, 0)
        row.progress:SetWidth(150)
        row.progress:SetJustifyH("RIGHT")
        row.name:SetPoint("RIGHT", row.progress, "LEFT", -6, 0)
        row:RegisterForClicks("LeftButtonUp", "RightButtonUp")
        frame.categories[index] = row
    end

    frame.missingTitle = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    frame.missingTitle:SetPoint("TOPLEFT", 15, -349)
    frame.missingTitle:SetText("NEXT MISSING OBJECTIVES")
    frame.missingTitle:SetTextColor(0.95, 0.74, 0.32)

    frame.rows = {}
    for index = 1, 12 do
        local row = CreateFrame("Button", nil, frame, "BackdropTemplate")
        row:SetPoint("TOPLEFT", 15, -366 - ((index - 1) * 25))
        row:SetPoint("TOPRIGHT", -15, -366 - ((index - 1) * 25))
        row:SetHeight(24)
        StyleRow(row, 0.018, 0.027, 0.033, index % 2 == 0 and 0.78 or 0.55)
        row.icon = row:CreateTexture(nil, "ARTWORK")
        row.icon:SetSize(18, 18)
        row.icon:SetPoint("LEFT", 8, 0)
        row.label = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
        row.label:SetPoint("LEFT", row.icon, "RIGHT", 7, 0)
        row.label:SetPoint("RIGHT", -5, 0)
        row.label:SetJustifyH("LEFT")
        row.label:SetWordWrap(false)
        row:Hide()
        frame.rows[index] = row
    end

    frame.footer = frame:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
    frame.footer:SetPoint("BOTTOMLEFT", 16, 14)
    frame.footer:SetJustifyH("LEFT")
    frame.roll = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
    frame.roll:SetSize(100, 23)
    frame.roll:SetPoint("BOTTOMRIGHT", -28, 9)
    frame.roll:SetText("Roll Zone")
    frame.roll:SetScript("OnClick", function() self:StartZoneRoll() end)
    frame.footer:SetPoint("RIGHT", frame.roll, "LEFT", -8, 0)

    frame.resize = CreateFrame("Button", nil, frame)
    frame.resize:SetSize(18, 18)
    frame.resize:SetPoint("BOTTOMRIGHT", -5, 5)
    frame.resize:SetNormalTexture("Interface/ChatFrame/UI-ChatIM-SizeGrabber-Up")
    frame.resize:SetHighlightTexture("Interface/ChatFrame/UI-ChatIM-SizeGrabber-Highlight")
    frame.resize:SetPushedTexture("Interface/ChatFrame/UI-ChatIM-SizeGrabber-Down")
    frame.resize:SetScript("OnMouseDown", function(_, button)
        if button == "LeftButton" then frame:StartSizing("BOTTOMRIGHT") end
    end)
    frame.resize:SetScript("OnMouseUp", function()
        frame:StopMovingOrSizing()
        if ZL_Saved.ui.minilist.compact then
            ZL_Saved.ui.minilist.compactWidth = frame:GetWidth()
            ZL_Saved.ui.minilist.compactHeight = frame:GetHeight()
        else
            ZL_Saved.ui.minilist.width = frame:GetWidth()
            ZL_Saved.ui.minilist.height = frame:GetHeight()
        end
        self:RefreshMiniList()
    end)
    frame.collapse:SetScript("OnClick", function()
        local state = ZL_Saved.ui.minilist
        if state.compact then
            state.compactWidth, state.compactHeight = frame:GetWidth(), frame:GetHeight()
            state.compact = false
            frame:SetResizeBounds(220, 88, 650, 800)
            frame:SetSize(math.max(220, state.width or 370), math.max(88, state.height or 540))
        else
            state.width, state.height = frame:GetWidth(), frame:GetHeight()
            state.compact = true
            frame:SetResizeBounds(220, 88, 650, 180)
            frame:SetSize(math.max(220, state.compactWidth or 300), math.max(88, state.compactHeight or 92))
        end
        frame.collapse:SetNormalTexture(state.compact and
            "Interface/Buttons/UI-Panel-CollapseButton-Up" or "Interface/Buttons/UI-Panel-HideButton-Up")
        frame.collapse:SetPushedTexture(state.compact and
            "Interface/Buttons/UI-Panel-CollapseButton-Up" or "Interface/Buttons/UI-Panel-HideButton-Down")
        self:RefreshMiniList()
    end)
    frame:Hide()
    self.MiniList = frame
end

function ZL:RefreshMiniList()
    local frame, theme = self.MiniList, self:GetTheme()
    if not frame or not ZL_Saved.settings.minilist.enabled then
        if frame then frame:Hide() end
        return
    end

    frame:Show()
    local mapID = self:GetActiveZone()
    if self.miniListMapID ~= mapID then
        self.miniListMapID = mapID
        self.miniCategoryKey = nil
    end
    local data = mapID and (self.ZoneData[mapID] or ZL_Saved.zoneSummaries[mapID])
    if mapID and not data and not ZL_Saved.completedZones[mapID] then data = self:ScanZone(mapID) end
    if mapID and not data and ZL_Saved.completedZones[mapID] then
        data = { mapID = mapID, name = self:GetZoneDefinition(mapID).name, done = 0, total = 0,
            categories = {}, summary = true, complete = true }
    end
    for _, row in ipairs(frame.rows) do
        row:Hide()
        row:SetScript("OnClick", nil)
        row:SetScript("OnEnter", nil)
        row:SetScript("OnLeave", nil)
    end
    for _, row in ipairs(frame.categories) do row:Hide() end
    local effectiveCompact = ZL_Saved.ui.minilist.compact or frame:GetHeight() < 145
    frame.categoriesTitle:SetShown(not effectiveCompact)
    frame.missingTitle:Hide()
    frame.footer:SetShown(not effectiveCompact)
    frame.roll:Hide()

    if not data then
        frame.title:SetText("No active zone")
        SetBar(frame.progress, 0, 0)
        frame.progress.text:SetText("Choose a starting zone")
        frame.footer:SetText("Open Zone Locked to begin")
        frame.roll:Hide()
        return
    end

    local done, total = self:GetProgress(data)
    frame.title:SetText(data.name)
    SetBar(frame.progress, done, total)

    if effectiveCompact then return end

    local missing = {}
    local missingTotal = 0
    local showMissing = ZL_Saved.settings.minilist.showMissing ~= false
    local categoryVisibility = ZL_Saved.settings.minilist.categories or {}
    local configuredCategoryCount = 0
    for _, category in ipairs(data.categories or {}) do
        if categoryVisibility[category.key] ~= false then configuredCategoryCount = configuredCategoryCount + 1 end
    end
    local maxCategoryRows = math.max(0, math.min(#frame.categories, math.floor((frame:GetHeight() - 145) / 30)))
    local displayedCategoryCount = math.min(configuredCategoryCount, maxCategoryRows)
    local missingTitleOffset = 109 + (displayedCategoryCount * 30)
    frame.missingTitle:ClearAllPoints()
    frame.missingTitle:SetPoint("TOPLEFT", 15, -missingTitleOffset)
    local missingRowsOffset = missingTitleOffset + 17
    local maxItems = showMissing and math.max(0, math.min(#frame.rows,
        math.floor((frame:GetHeight() - missingRowsOffset - 40) / 25))) or 0
    frame.missingTitle:SetShown(showMissing and maxItems > 0)
    local visibleCategoryIndex = 0
    for _, category in ipairs(data.categories or {}) do
        local categoryAllowed = categoryVisibility[category.key] ~= false
        local categoryRow
        if categoryAllowed and visibleCategoryIndex < maxCategoryRows then
            visibleCategoryIndex = visibleCategoryIndex + 1
            categoryRow = frame.categories[visibleCategoryIndex]
        end
        if categoryRow then
            categoryRow:ClearAllPoints()
            categoryRow:SetPoint("TOPLEFT", 15, -101 - ((visibleCategoryIndex - 1) * 30))
            categoryRow:SetPoint("TOPRIGHT", -15, -101 - ((visibleCategoryIndex - 1) * 30))
            local categoryKey = category.key
            local categoryDone, categoryTotal = self:GetCategoryProgress(category)
            local percent = categoryTotal > 0 and math.floor((categoryDone / categoryTotal) * 100 + 0.5) or 100
            local enabled = self:IsCategoryEnabled(category.key)
            categoryRow.icon:SetTexture(CATEGORY_ICONS[category.key] or 134400)
            categoryRow.icon:SetDesaturated(not enabled)
            categoryRow.name:SetText(category.name)
            categoryRow.name:SetTextColor(enabled and theme.secondary[1] or 0.50,
                enabled and theme.secondary[2] or 0.50, enabled and theme.secondary[3] or 0.50)
            categoryRow.progress:SetText(string.format("%d%%  |cff9fb0ba%d/%d|r%s", percent, categoryDone, categoryTotal,
                enabled and "" or "  |cff777777excluded|r"))
            categoryRow.branch:SetText(self.miniCategoryKey == categoryKey and ">" or "-")
            categoryRow:SetScript("OnClick", function(_, mouseButton)
                if mouseButton == "RightButton" then
                    if data.summary then
                        StaticPopup_Show("ZONELOCKED_LOAD_COMPLETED_ZONE", data.name, nil,
                            { mapID = mapID, categoryKey = categoryKey })
                        return
                    end
                    self.selectedMapID = mapID
                    self.selectedCategoryKey = categoryKey
                    self.UI:Show()
                    self.UI.scroll:SetVerticalScroll(0)
                    self._uiSignature = nil
                    self:RefreshUI()
                elseif IsAltKeyDown() then
                    self:AddCategoryWaypoint(category)
                else
                    self.miniCategoryKey = self.miniCategoryKey == categoryKey and nil or categoryKey
                    self:RefreshMiniList()
                end
            end)
            categoryRow:SetScript("OnEnter", function(row)
                row:SetBackdropColor(0.05, 0.11, 0.14, 0.98)
                GameTooltip:SetOwner(row, "ANCHOR_LEFT")
                GameTooltip:SetText(category.name)
                GameTooltip:AddLine("Left-click to filter missing objectives.", 1, 1, 1, true)
                GameTooltip:AddLine("Right-click to open this category in Zone Locked.", 0.35, 0.8, 1, true)
                GameTooltip:AddLine("Alt-click to waypoint its next missing objective.", 1, 0.82, 0.35, true)
                GameTooltip:Show()
            end)
            categoryRow:SetScript("OnLeave", function(row)
                row:SetBackdropColor(0.025, 0.045, 0.055, 0.88)
                GameTooltip_Hide()
            end)
            if categoryDone == categoryTotal then
                categoryRow.progress:SetTextColor(unpack(theme.secondary))
                categoryRow:SetBackdropBorderColor(unpack(theme.secondary))
            else
                categoryRow.progress:SetTextColor(enabled and theme.accent[1] or 0.55,
                    enabled and theme.accent[2] or 0.55, enabled and theme.accent[3] or 0.55)
                categoryRow:SetBackdropBorderColor(unpack(theme.border))
            end
            categoryRow:Show()

            if enabled and (not self.miniCategoryKey or self.miniCategoryKey == categoryKey) then
                missingTotal = missingTotal + math.max(0, categoryTotal - categoryDone)
                if #missing < maxItems then
                    local objectiveGroups = { { objectives = category.objectives, label = category.name } }
                    for _, instance in ipairs(category.instances or {}) do
                        for _, childCategory in ipairs(instance.categories or {}) do
                            if self:IsCategoryEnabled(childCategory.key) then
                                objectiveGroups[#objectiveGroups + 1] = {
                                    objectives = childCategory.objectives,
                                    label = instance.name .. " / " .. childCategory.name,
                                }
                            end
                        end
                    end
                    for _, group in ipairs(objectiveGroups) do
                        for _, objective in ipairs(group.objectives or {}) do
                            if not objective.complete then
                                missing[#missing + 1] = { objective = objective, category = group.label }
                                if #missing >= maxItems then break end
                            end
                        end
                        if #missing >= maxItems then break end
                    end
                end
            end
        end
    end

    for index = 1, math.min(maxItems, #missing) do
        local item = missing[index]
        local row = frame.rows[index]
        row:ClearAllPoints()
        row:SetPoint("TOPLEFT", 15, -missingRowsOffset - ((index - 1) * 25))
        row:SetPoint("TOPRIGHT", -15, -missingRowsOffset - ((index - 1) * 25))
        row.icon:SetTexture(item.objective.icon or 134400)
        row.icon:SetDesaturated(true)
        row.label:SetText("|cff7896a8" .. item.category .. ":|r " .. item.objective.name)
        row:SetScript("OnEnter", function(button)
            button:SetBackdropColor(0.05, 0.11, 0.14, 0.95)
            self:ShowObjectiveTooltip(button, item.objective, "ANCHOR_LEFT")
        end)
        row:SetScript("OnLeave", function(button)
            button:SetBackdropColor(0.018, 0.027, 0.033, index % 2 == 0 and 0.78 or 0.55)
            GameTooltip_Hide()
        end)
        row:SetScript("OnClick", function()
            self:HandleObjectiveClick(item.objective)
        end)
        row:Show()
    end

    if not showMissing then
        frame.footer:SetText("Right-click a category to open it.")
    elseif missingTotal == 0 then
        frame.footer:SetText("|cff55ff88All counted categories complete.|r")
    elseif missingTotal > maxItems then
        frame.footer:SetText(string.format("Showing %d of %d missing", math.min(maxItems, #missing), missingTotal))
    else
        frame.footer:SetText(string.format("%d objective%s remaining", missingTotal, missingTotal == 1 and "" or "s"))
    end
    frame.roll:SetShown(self:IsZoneComplete(mapID) and not self:GetNextZone())
end
